element-vir
Version:
Heroic. Reactive. Declarative. Type safe. Web components without compromise.
115 lines (114 loc) • 3.4 kB
JavaScript
import { AsyncDirective, directive } from '../../lit-exports/all-lit-exports.js';
import { assertIsElementPartInfo } from './directive-helpers.js';
const directiveName = 'onResize';
/**
* A directive that fires its listener any time the element that it's attached to is resized. This
* uses the [built-in `ResizeObserver`
* API](https://developer.mozilla.org/docs/Web/API/ResizeObserver), so it is very efficient.
*
* @category Directives
* @example
*
* ```ts
* import {html, defineElement, onResize} from 'element-vir';
*
* const MyElement = defineElement()({
* tagName: 'my-element',
* render() {
* return html`
* <div
* ${onResize((size, element) => {
* console.log('resized!', element, size);
* })}
* >
* Some div
* </div>
* `;
* },
* });
* ```
*/
export const onResize = directive(class extends AsyncDirective {
element;
resizeObserver = new ResizeObserver((entries) => {
if (this.element && this.callback) {
handleOnResizeCallback(this.element, this.callback, entries);
}
});
callback;
constructor(partInfo) {
super(partInfo);
assertIsElementPartInfo(partInfo, directiveName);
}
update(partInfo, [callback]) {
assertIsElementPartInfo(partInfo, directiveName);
this.callback = callback;
const newElement = partInfo.element;
const oldElement = this.element;
// if the element changes we need to observe the new one
if (newElement !== oldElement) {
this.element = newElement;
if (oldElement) {
this.resizeObserver.unobserve(oldElement);
}
this.resizeObserver.observe(newElement);
}
return this.render(callback);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
render(callback) {
return undefined;
}
disconnected() {
this.resizeObserver.disconnect();
}
reconnected() {
if (this.element) {
this.resizeObserver.observe(this.element);
}
}
});
function handleOnResizeCallback(element, callback, entries) {
const resizeEntry = entries[0];
if (!resizeEntry) {
console.error(entries);
throw new Error('Resize observation triggered but the first entry was empty.');
}
void callback({
target: resizeEntry.target,
contentRect: resizeEntry.contentRect,
}, element);
}
/**
* A function that attaches a
* [`ResizeObserver`](https://developer.mozilla.org/docs/Web/API/ResizeObserver) to any given
* element, so it is very efficient.
*
* @category Directives
* @example
*
* ```ts
* import {html, defineElement, attachOnResize} from 'element-vir';
*
* const MyElement = defineElement()({
* tagName: 'my-element',
* render({host}) {
* attachOnResize(host, (size, element) => {
* console.log('resized!', element, size);
* });
*
* return '';
* },
* });
* ```
*/
export function attachOnResize(element, callback) {
const resizeObserver = new ResizeObserver((entries) => {
handleOnResizeCallback(element, callback, entries);
});
resizeObserver.observe(element);
return {
resizeObserver,
element,
};
}