From 75ba33f115e3c3d4228d06c35def6159bfc1d515 Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Fri, 14 Apr 2023 17:01:50 +0300 Subject: [PATCH 1/5] feat(esl-utils): Support for window as a target for ESLResizeObserverTarget --- .../core/targets/resize.adapter.event.ts | 35 ++++++++++++++--- .../core/targets/resize.adapter.ts | 39 ++++++++++++------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts index 3e80cdbe0..1d1563c51 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts @@ -1,4 +1,6 @@ + import {overrideEvent} from '../../../esl-utils/dom/events/misc'; +import {getWindowRect} from '../../../esl-utils/dom/window'; import type {ESLResizeObserverTarget} from './resize.adapter'; @@ -33,7 +35,7 @@ export class ESLElementResizeEvent extends UIEvent implements ResizeObserverEntr */ public readonly devicePixelContentBoxSize: readonly ResizeObserverSize[]; - protected constructor(target: Element) { + protected constructor(target: EventTarget) { super('resize', {bubbles: false, cancelable: false}); overrideEvent(this, 'target', target); } @@ -52,10 +54,33 @@ export class ESLElementResizeEvent extends UIEvent implements ResizeObserverEntr return event; } - // /** Creates {@link ESLElementResizeEvent} from resize {@link Event} */ - // public static fromEvent(event: UIEvent): ESLElementResizeEvent { - // // TODO: converter - // } + /** Creates {@link ESLElementResizeEvent} from resize {@link Event} */ + public static fromEvent(e: UIEvent): ESLElementResizeEvent | never { + const {target} = e; + if (!target) throw new Error('Event must be at object with a `target` property'); + let borderBoxSize: ResizeObserverSize[]; + let contentBoxSize: ResizeObserverSize[]; + + if (target instanceof Element) { + const rect = target.getBoundingClientRect(); + contentBoxSize = [{inlineSize: target.clientWidth, blockSize: target.clientHeight}]; + borderBoxSize = [{inlineSize: rect.width, blockSize: rect.height}]; + } else if (target instanceof Window) { + const wndRect = getWindowRect(); + contentBoxSize = [{inlineSize: wndRect.width, blockSize: wndRect.height}]; + borderBoxSize = contentBoxSize; + } else throw new Error('Event target must be an element or window object'); + + const contentRect = new DOMRectReadOnly(0, 0, contentBoxSize[0].inlineSize, contentBoxSize[0].blockSize); + const devicePixelContentBoxSize = [{ + inlineSize: contentBoxSize[0].inlineSize * window.devicePixelRatio, + blockSize: contentBoxSize[0].blockSize * window.devicePixelRatio + }]; + + const event = new ESLElementResizeEvent(target); + Object.assign(event, {contentRect, borderBoxSize, contentBoxSize, devicePixelContentBoxSize}); + return event; + } } declare global { diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.ts index 3afcfc8c2..f16a45a8d 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.ts @@ -1,5 +1,6 @@ import {SyntheticEventTarget} from '../../../esl-utils/dom/events/target'; import {resolveDomTarget} from '../../../esl-utils/abstract/dom-target'; +import {ESLEventUtils} from '../api'; import {ESLElementResizeEvent} from './resize.adapter.event'; import type {ESLDomElementTarget} from '../../../esl-utils/abstract/dom-target'; @@ -9,36 +10,36 @@ export {ESLElementResizeEvent}; /** Adapter class for {@link ResizeObserver} that implements {@link EventTarget} */ export class ESLResizeObserverTarget extends SyntheticEventTarget { /** {@link ESLResizeObserverTarget} instances holder */ - protected static readonly mapping = new WeakMap(); + protected static readonly mapping = new WeakMap(); /** {@link ResizeObserver} instance to observe DOM element related to {@link ESLResizeObserverTarget} */ protected static readonly observer$$ = new ResizeObserver((changes: ResizeObserverEntry[]) => changes.forEach(this.handleChange, this) ); - /** Observed {@link Element} of the {@link ESLResizeObserverTarget} instance */ - public readonly target: Element; + /** Observed {@link Element} of the {@link ESLResizeObserverTarget} instance or {@link Window} object */ + public readonly target: Element | Window; /** Internal method to handle {@link ResizeObserver} entry change */ protected static handleChange( this: typeof ESLResizeObserverTarget, - entry: ResizeObserverEntry + entry: ResizeObserverEntry | UIEvent ): void { - const adapter = this.mapping.get(entry.target); + const adapter = ESLResizeObserverTarget.mapping.get(entry.target as (Element | Window)); if (!adapter) return; - adapter.dispatchEvent(ESLElementResizeEvent.fromEntry(entry)); + adapter.dispatchEvent(entry instanceof UIEvent ? ESLElementResizeEvent.fromEvent(entry)! : ESLElementResizeEvent.fromEntry(entry)); } /** Creates {@link ESLResizeObserverTarget} instance for the {@link ESLDomElementTarget} */ - public static for(target: ESLDomElementTarget): ESLResizeObserverTarget { + public static for(target: ESLDomElementTarget | Window): ESLResizeObserverTarget { return new ESLResizeObserverTarget(target); } /** * Creates {@link ESLResizeObserverTarget} for the {@link ESLDomElementTarget}. - * Note the {@link ESLResizeObserverTarget} instances are singletons relatively to the {@link Element} + * Note the {@link ESLResizeObserverTarget} instances are singletons relatively to the {@link Element} or {@link Window} */ - protected constructor(target: ESLDomElementTarget) { - target = resolveDomTarget(target); + protected constructor(target: ESLDomElementTarget | Window) { + if (!(target instanceof Window)) target = resolveDomTarget(target); const instance = ESLResizeObserverTarget.mapping.get(target); if (instance) return instance; @@ -47,7 +48,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { ESLResizeObserverTarget.mapping.set(this.target, this); } - /** Subscribes to the observed target {@link Element} changes */ + /** Subscribes to the observed target {@link Element} or {@link Window} changes */ public override addEventListener(callback: EventListener): void; public override addEventListener(event: 'resize', callback: EventListener): void; public override addEventListener(event: any, callback: EventListener = event): void { @@ -58,10 +59,15 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { super.addEventListener('resize', callback); if (this.getEventListeners('resize').length > 1) return; - ESLResizeObserverTarget.observer$$.observe(this.target); + + if (this.target instanceof Window) { + ESLEventUtils.subscribe(this, {event: 'resize', target: window}, ESLResizeObserverTarget.handleChange); + } else { + ESLResizeObserverTarget.observer$$.observe(this.target); + } } - /** Unsubscribes from the observed target {@link Element} changes */ + /** Unsubscribes from the observed target {@link Element} or {@link Window} changes */ public override removeEventListener(callback: EventListener): void; public override removeEventListener(event: 'resize', callback: EventListener): void; public override removeEventListener(event: any, callback: EventListener = event): void { @@ -69,6 +75,11 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { super.removeEventListener('resize', callback); if (this.hasEventListener('resize')) return; - ESLResizeObserverTarget.observer$$.unobserve(this.target); + + if (this.target instanceof Window) { + ESLEventUtils.unsubscribe(this); + } else { + ESLResizeObserverTarget.observer$$.unobserve(this.target); + } } } From 6be310aa50aaa480018303c3b12c6f968ae12fff Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Fri, 14 Apr 2023 17:13:21 +0300 Subject: [PATCH 2/5] chore(esl-utils): code refactoring --- .../esl-event-listener/core/targets/resize.adapter.event.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts index 1d1563c51..680ae6f80 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts @@ -1,4 +1,3 @@ - import {overrideEvent} from '../../../esl-utils/dom/events/misc'; import {getWindowRect} from '../../../esl-utils/dom/window'; From 05945a9f296c1337afff5241fdd0c6cfbcf44ddc Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Fri, 14 Apr 2023 17:26:53 +0300 Subject: [PATCH 3/5] chore(esl-utils): code refactoring --- src/modules/esl-event-listener/core/targets/resize.adapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.ts index f16a45a8d..ebc869fb5 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.ts @@ -77,7 +77,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { if (this.hasEventListener('resize')) return; if (this.target instanceof Window) { - ESLEventUtils.unsubscribe(this); + ESLEventUtils.unsubscribe(this, 'resize'); } else { ESLResizeObserverTarget.observer$$.unobserve(this.target); } From 341e23ee1221a6e6b81429ad7b83306adf1c0765 Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Mon, 24 Apr 2023 18:05:41 +0300 Subject: [PATCH 4/5] chore(esl-utils): code refactoring --- .../core/targets/resize.adapter.event.ts | 2 +- .../core/targets/resize.adapter.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts index 680ae6f80..64853f701 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts @@ -54,7 +54,7 @@ export class ESLElementResizeEvent extends UIEvent implements ResizeObserverEntr } /** Creates {@link ESLElementResizeEvent} from resize {@link Event} */ - public static fromEvent(e: UIEvent): ESLElementResizeEvent | never { + public static fromEvent(e: Event): ESLElementResizeEvent | never { const {target} = e; if (!target) throw new Error('Event must be at object with a `target` property'); let borderBoxSize: ResizeObserverSize[]; diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.ts index ebc869fb5..6b28dff3d 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.ts @@ -1,6 +1,6 @@ import {SyntheticEventTarget} from '../../../esl-utils/dom/events/target'; import {resolveDomTarget} from '../../../esl-utils/abstract/dom-target'; -import {ESLEventUtils} from '../api'; +import {ESLEventListener} from '../listener'; import {ESLElementResizeEvent} from './resize.adapter.event'; import type {ESLDomElementTarget} from '../../../esl-utils/abstract/dom-target'; @@ -22,11 +22,12 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { /** Internal method to handle {@link ResizeObserver} entry change */ protected static handleChange( this: typeof ESLResizeObserverTarget, - entry: ResizeObserverEntry | UIEvent + entry: ResizeObserverEntry | Event ): void { - const adapter = ESLResizeObserverTarget.mapping.get(entry.target as (Element | Window)); + const adapter = (this instanceof ESLResizeObserverTarget ? (this.constructor as typeof ESLResizeObserverTarget) : this) + .mapping.get(entry.target as (Element | Window)); if (!adapter) return; - adapter.dispatchEvent(entry instanceof UIEvent ? ESLElementResizeEvent.fromEvent(entry)! : ESLElementResizeEvent.fromEntry(entry)); + adapter.dispatchEvent(entry instanceof Event ? ESLElementResizeEvent.fromEvent(entry)! : ESLElementResizeEvent.fromEntry(entry)); } /** Creates {@link ESLResizeObserverTarget} instance for the {@link ESLDomElementTarget} */ @@ -61,7 +62,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { if (this.getEventListeners('resize').length > 1) return; if (this.target instanceof Window) { - ESLEventUtils.subscribe(this, {event: 'resize', target: window}, ESLResizeObserverTarget.handleChange); + ESLEventListener.subscribe(this, ESLResizeObserverTarget.handleChange, {event: 'resize', target: window}); } else { ESLResizeObserverTarget.observer$$.observe(this.target); } @@ -77,7 +78,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { if (this.hasEventListener('resize')) return; if (this.target instanceof Window) { - ESLEventUtils.unsubscribe(this, 'resize'); + ESLEventListener.get(this, ESLResizeObserverTarget.handleChange, {event: 'resize', target: window}).forEach((listener) => listener.unsubscribe()); } else { ESLResizeObserverTarget.observer$$.unobserve(this.target); } From 464e5d954a3a2453e03346233ff6b11ba345d151 Mon Sep 17 00:00:00 2001 From: Feoktist Shovchko Date: Thu, 27 Apr 2023 19:46:32 +0300 Subject: [PATCH 5/5] chore(esl-utils): code refactoring --- .../core/targets/resize.adapter.event.ts | 4 ++-- .../esl-event-listener/core/targets/resize.adapter.ts | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts index 64853f701..1b6aef06e 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.event.ts @@ -34,7 +34,7 @@ export class ESLElementResizeEvent extends UIEvent implements ResizeObserverEntr */ public readonly devicePixelContentBoxSize: readonly ResizeObserverSize[]; - protected constructor(target: EventTarget) { + protected constructor(target: Element | Window) { super('resize', {bubbles: false, cancelable: false}); overrideEvent(this, 'target', target); } @@ -56,7 +56,7 @@ export class ESLElementResizeEvent extends UIEvent implements ResizeObserverEntr /** Creates {@link ESLElementResizeEvent} from resize {@link Event} */ public static fromEvent(e: Event): ESLElementResizeEvent | never { const {target} = e; - if (!target) throw new Error('Event must be at object with a `target` property'); + if (!target) throw new Error('[ESLElementResizeEvent]: original event should have a `target`'); let borderBoxSize: ResizeObserverSize[]; let contentBoxSize: ResizeObserverSize[]; diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.ts index 6b28dff3d..686212a0b 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.ts @@ -24,10 +24,9 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { this: typeof ESLResizeObserverTarget, entry: ResizeObserverEntry | Event ): void { - const adapter = (this instanceof ESLResizeObserverTarget ? (this.constructor as typeof ESLResizeObserverTarget) : this) - .mapping.get(entry.target as (Element | Window)); + const adapter = this.mapping.get(entry.target as Element | Window); if (!adapter) return; - adapter.dispatchEvent(entry instanceof Event ? ESLElementResizeEvent.fromEvent(entry)! : ESLElementResizeEvent.fromEntry(entry)); + adapter.dispatchEvent(entry instanceof Event ? ESLElementResizeEvent.fromEvent(entry) : ESLElementResizeEvent.fromEntry(entry)); } /** Creates {@link ESLResizeObserverTarget} instance for the {@link ESLDomElementTarget} */ @@ -62,7 +61,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { if (this.getEventListeners('resize').length > 1) return; if (this.target instanceof Window) { - ESLEventListener.subscribe(this, ESLResizeObserverTarget.handleChange, {event: 'resize', target: window}); + ESLEventListener.subscribe(this, (e: Event) => (this.constructor as typeof ESLResizeObserverTarget).handleChange(e), {event: 'resize', target: window}); } else { ESLResizeObserverTarget.observer$$.observe(this.target); } @@ -78,7 +77,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { if (this.hasEventListener('resize')) return; if (this.target instanceof Window) { - ESLEventListener.get(this, ESLResizeObserverTarget.handleChange, {event: 'resize', target: window}).forEach((listener) => listener.unsubscribe()); + ESLEventListener.get(this, 'resize').forEach((listener) => listener.unsubscribe()); } else { ESLResizeObserverTarget.observer$$.unobserve(this.target); }