🎨 Simplified resize observer singleton

pull/8022/head
TheCymaera 4 years ago
parent 2c5c7a10b7
commit c0572ba4c0

@ -416,12 +416,12 @@ function get_value_from_dom(
// <div bind:contentRect|contentBoxSize|borderBoxSize|devicePixelContentBoxSize> // <div bind:contentRect|contentBoxSize|borderBoxSize|devicePixelContentBoxSize>
if (regex_box_size.test(name)) { if (regex_box_size.test(name)) {
const functionName = { const functionName = ({
"contentRect": "get_content_rect", "contentRect": "get_content_rect",
"contentBoxSize": "get_content_box_size", "contentBoxSize": "get_content_box_size",
"borderBoxSize": "get_border_box_size", "borderBoxSize": "get_border_box_size",
"devicePixelContentBoxSize": "get_device_pixel_content_box_size", "devicePixelContentBoxSize": "get_device_pixel_content_box_size",
} })[name];
return x`@${functionName}(this)`; return x`@${functionName}(this)`;
} }

@ -12,7 +12,7 @@ import { namespaces } from '../../../../utils/namespaces';
import AttributeWrapper from './Attribute'; import AttributeWrapper from './Attribute';
import StyleAttributeWrapper from './StyleAttribute'; import StyleAttributeWrapper from './StyleAttribute';
import SpreadAttributeWrapper from './SpreadAttribute'; import SpreadAttributeWrapper from './SpreadAttribute';
import { regex_dimensions, regex_starts_with_newline, regex_backslashes, regex_border_box_size, regex_content_box_size, regex_device_pixel_content_box_size } from '../../../../utils/patterns'; import { regex_dimensions, regex_starts_with_newline, regex_backslashes, regex_border_box_size, regex_content_box_size, regex_device_pixel_content_box_size, regex_content_rect } from '../../../../utils/patterns';
import Binding from './Binding'; import Binding from './Binding';
import add_to_set from '../../../utils/add_to_set'; import add_to_set from '../../../utils/add_to_set';
import { add_event_handler } from '../shared/add_event_handlers'; import { add_event_handler } from '../shared/add_event_handlers';
@ -71,15 +71,20 @@ const events = [
}, },
{ {
event_names: ['elementresizeobserveborderbox'], event_names: ['elementresizeobservecontentrect'],
filter: (_node: Element, name: string) => filter: (_node: Element, name: string) =>
regex_border_box_size.test(name) regex_content_rect.test(name)
}, },
{ {
event_names: ['elementresizeobservecontentbox'], event_names: ['elementresizeobservecontentbox'],
filter: (_node: Element, name: string) => filter: (_node: Element, name: string) =>
regex_content_box_size.test(name) regex_content_box_size.test(name)
}, },
{
event_names: ['elementresizeobserveborderbox'],
filter: (_node: Element, name: string) =>
regex_border_box_size.test(name)
},
{ {
event_names: ['elementresizeobservedevicepixelcontentbox'], event_names: ['elementresizeobservedevicepixelcontentbox'],
filter: (_node: Element, name: string) => filter: (_node: Element, name: string) =>
@ -710,6 +715,7 @@ export default class ElementWrapper extends Wrapper {
const functionName = ({ const functionName = ({
"elementresize": "add_iframe_resize_listener", "elementresize": "add_iframe_resize_listener",
"elementresizeobservecontentrect": "add_content_rect_observer",
"elementresizeobservecontentbox": "add_content_box_observer", "elementresizeobservecontentbox": "add_content_box_observer",
"elementresizeobserveborderbox": "add_border_box_observer", "elementresizeobserveborderbox": "add_border_box_observer",
"elementresizeobservedevicepixelcontentbox": "add_device_pixel_content_box_observer", "elementresizeobservedevicepixelcontentbox": "add_device_pixel_content_box_observer",

@ -23,7 +23,8 @@ export const regex_invalid_variable_identifier_characters = /[^a-zA-Z0-9_$]/g;
export const regex_dimensions = /^(?:offset|client)(?:Width|Height)$/; export const regex_dimensions = /^(?:offset|client)(?:Width|Height)$/;
export const regex_content_box_size = /^(?:contentRect|contentBoxSize)$/; export const regex_content_rect = /^(?:contentRect)$/;
export const regex_content_box_size = /^(?:contentBoxSize)$/;
export const regex_border_box_size = /^(?:borderBoxSize)$/; export const regex_border_box_size = /^(?:borderBoxSize)$/;
export const regex_device_pixel_content_box_size = /^(?:devicePixelContentBoxSize)$/; export const regex_device_pixel_content_box_size = /^(?:devicePixelContentBoxSize)$/;
export const regex_box_size = /^(?:contentRect|contentBoxSize|borderBoxSize|devicePixelContentBoxSize)$/ export const regex_box_size = /^(?:contentRect|contentBoxSize|borderBoxSize|devicePixelContentBoxSize)$/

@ -1,4 +1,4 @@
const MapImplementation = "WeakMap" in window ? WeakMap : Map; const MapImplementation = "WeakMap" in window ? WeakMap : ("Map" in window ? Map : undefined);
/** /**
* Resize observer singleton * Resize observer singleton
@ -8,53 +8,37 @@ export class ResizeObserverSingleton {
constructor(readonly options?: ResizeObserverOptions) {} constructor(readonly options?: ResizeObserverOptions) {}
addListener(element: Element, callback: Callback) { addListener(element: Element, callback: Callback) {
if (!this._subscriptions.has(element)) { this._subscriptions.set(element, new Subscription(callback));
this._subscriptions.set(element, new Subscription(new Set([callback]))); this._getObserver().observe(element, this.options);
this._getObserver().observe(element, this.options);
} else {
const subscription = this._subscriptions.get(element)!;
subscription.listeners.add(callback);
callback.call(element, subscription.lastEntry!);
}
} }
removeListener(element: Element, callback: Callback) { removeListener(element: Element) {
const subscription = this._subscriptions.get(element); this._subscriptions.delete(element);
if (!subscription) return; this._getObserver().unobserve(element);
if (subscription.listeners.size > 1) {
subscription.listeners.delete(callback);
} else {
this._subscriptions.delete(element);
this._getObserver().unobserve(element);
}
} }
getLastEntry(element: Element) { getLastEntry(element: Element) {
return this._subscriptions.get(element)?.lastEntry; return this._subscriptions.get(element)?.lastEntry;
} }
private readonly _subscriptions: WeakMap<Element, Subscription> = new MapImplementation(); private readonly _subscriptions: WeakMap<Element, Subscription> = MapImplementation ? new MapImplementation() : undefined;
private _observer: ResizeObserver|undefined = undefined; private _observer: ResizeObserver|undefined = undefined;
private _getObserver() { private _getObserver() {
return this._observer ?? (this._observer = new ResizeObserver((entries)=>{ return this._observer ?? (this._observer = new ResizeObserver((entries)=>{
for (const entry of entries) { for (const entry of entries) {
const element = entry.target; const subscription = this._subscriptions.get(entry.target)!;
const subscription = this._subscriptions.get(element);
if (!subscription) continue;
subscription.lastEntry = entry; subscription.lastEntry = entry;
for (const callback of subscription.listeners) callback.call(element, entry); subscription.listener(entry);
} }
})); }));
} }
} }
type Callback = (this: Element, entry: ResizeObserverEntry)=>any; type Callback = (entry: ResizeObserverEntry)=>any;
class Subscription { class Subscription {
// TODO: consider using Array instead of Set?
// If we can guarantee one listener per element, we can simplify even more.
constructor( constructor(
readonly listeners: Set<Callback>, readonly listener: Callback,
public lastEntry?: ResizeObserverEntry, public lastEntry?: ResizeObserverEntry,
) {} ) {}
} }

@ -641,27 +641,33 @@ export function add_iframe_resize_listener(node: HTMLElement, fn: () => void) {
}; };
} }
const resize_observer_content_rect = new ResizeObserverSingleton({ box: "content-box" });
const resize_observer_content_box = new ResizeObserverSingleton({ box: "content-box" }); const resize_observer_content_box = new ResizeObserverSingleton({ box: "content-box" });
const resize_observer_border_box = new ResizeObserverSingleton({ box: "border-box" }); const resize_observer_border_box = new ResizeObserverSingleton({ box: "border-box" });
const resize_observer_device_pixel_content_box = new ResizeObserverSingleton({ box: "device-pixel-content-box" }); const resize_observer_device_pixel_content_box = new ResizeObserverSingleton({ box: "device-pixel-content-box" });
export function add_content_rect_observer(node: Element, fn: () => void) {
resize_observer_content_rect.addListener(node, fn);
return ()=> resize_observer_content_rect.removeListener(node);
}
export function add_content_box_observer(node: Element, fn: () => void) { export function add_content_box_observer(node: Element, fn: () => void) {
resize_observer_content_box.addListener(node, fn); resize_observer_content_box.addListener(node, fn);
return ()=> resize_observer_content_box.removeListener(node, fn); return ()=> resize_observer_content_box.removeListener(node);
} }
export function add_border_box_observer(node: Element, fn: () => void) { export function add_border_box_observer(node: Element, fn: () => void) {
resize_observer_border_box.addListener(node, fn); resize_observer_border_box.addListener(node, fn);
return ()=> resize_observer_border_box.removeListener(node, fn); return ()=> resize_observer_border_box.removeListener(node);
} }
export function add_device_pixel_content_box_observer(node: Element, fn: () => void) { export function add_device_pixel_content_box_observer(node: Element, fn: () => void) {
resize_observer_device_pixel_content_box.addListener(node, fn); resize_observer_device_pixel_content_box.addListener(node, fn);
return ()=> resize_observer_device_pixel_content_box.removeListener(node, fn); return ()=> resize_observer_device_pixel_content_box.removeListener(node);
} }
export function get_content_rect(node: Element) { export function get_content_rect(node: Element) {
return resize_observer_content_box.getLastEntry(node)?.contentRect; return resize_observer_content_rect.getLastEntry(node)?.contentRect;
} }
export function get_content_box_size(node: Element) { export function get_content_box_size(node: Element) {

Loading…
Cancel
Save