Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds resize observer functionality #5963

Closed
wants to merge 17 commits into from
Closed
5 changes: 4 additions & 1 deletion src/compiler/compile/nodes/Binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import get_object from '../utils/get_object';
import Expression from './shared/Expression';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import {dimensions} from '../../utils/patterns';
import { dimensions, sizing_border_box, sizing_content_box, sizing_device_pixel_content_box } from '../../utils/patterns';
import { Node as ESTreeNode } from 'estree';
import { TemplateNode } from '../../interfaces';
import Element from './Element';
Expand Down Expand Up @@ -87,6 +87,9 @@ export default class Binding extends Node {

this.is_readonly =
dimensions.test(this.name) ||
sizing_border_box.test(this.name) ||
sizing_content_box.test(this.name) ||
sizing_device_pixel_content_box.test(this.name) ||
(isElement(parent) &&
((parent.is_media_node() && read_only_media_attributes.has(this.name)) ||
(parent.name === 'input' && type === 'file')) /* TODO others? */);
Expand Down
9 changes: 7 additions & 2 deletions src/compiler/compile/nodes/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import StyleDirective from './StyleDirective';
import Text from './Text';
import { namespaces } from '../../utils/namespaces';
import map_children from './shared/map_children';
import { dimensions } from '../../utils/patterns';
import { dimensions, sizing_border_box, sizing_content_box, sizing_device_pixel_content_box } from '../../utils/patterns';
import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list';
import Let from './Let';
Expand Down Expand Up @@ -734,7 +734,12 @@ export default class Element extends Node {
} else if (contenteditable && !contenteditable.is_static) {
return component.error(contenteditable, compiler_errors.dynamic_contenteditable_attribute);
}
} else if (name !== 'this') {
} else if (
name !== 'this' &&
!sizing_border_box.test(name) &&
!sizing_content_box.test(name) &&
!sizing_device_pixel_content_box.test(name)
) {
return component.error(binding, compiler_errors.invalid_binding(binding.name));
}
});
Expand Down
47 changes: 46 additions & 1 deletion src/compiler/compile/render_dom/wrappers/Element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { namespaces } from '../../../../utils/namespaces';
import AttributeWrapper from './Attribute';
import StyleAttributeWrapper from './StyleAttribute';
import SpreadAttributeWrapper from './SpreadAttribute';
import { dimensions } from '../../../../utils/patterns';
import { dimensions, sizing_border_box, sizing_content_box, sizing_device_pixel_content_box } from '../../../../utils/patterns';
import Binding from './Binding';
import add_to_set from '../../../utils/add_to_set';
import { add_event_handler } from '../shared/add_event_handlers';
Expand Down Expand Up @@ -62,6 +62,21 @@ const events = [
filter: (_node: Element, name: string) =>
dimensions.test(name)
},
{
event_names: ['elementresizeobserveborderbox'],
filter: (_node: Element, name: string) =>
sizing_border_box.test(name)
},
{
event_names: ['elementresizeobservecontentbox'],
filter: (_node: Element, name: string) =>
sizing_content_box.test(name)
},
{
event_names: ['elementresizeobservedevicepixelcontentbox'],
filter: (_node: Element, name: string) =>
sizing_device_pixel_content_box.test(name)
},

// media events
{
Expand Down Expand Up @@ -542,6 +557,36 @@ export default class ElementWrapper extends Wrapper {
block.chunks.destroy.push(
b`${resize_listener}();`
);
} else if (name.startsWith('elementresizeobserve')) {
const resize_observer = block.get_unique_name(`${this.var.name}_resize_observer`);
block.add_variable(resize_observer);

let box = 'content-box'
switch (name) {
case 'elementresizeobserveborderbox':
box = 'border-box';
break;
case 'elementresizeobservecontentbox':
box = 'content-box';
break;
case 'elementresizeobservedevicepixelcontentbox':
box = 'device-pixel-content-box';
break;
}

if (renderer.options.dev) {
block.chunks.mount.push(
b`${resize_observer} = @add_resize_observer_dev(${this.var}, ${callee}, { box: "${box}" });`
);
} else {
block.chunks.mount.push(
b`${resize_observer} = @add_resize_observer(${this.var}, ${callee}, { box: "${box}" });`
);
}
Comment on lines +577 to +585
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you dont have to do this here, any runtime function suffixed with _dev will be automatically used when the dev mode is true

Suggested change
if (renderer.options.dev) {
block.chunks.mount.push(
b`${resize_observer} = @add_resize_observer_dev(${this.var}, ${callee}, { box: "${box}" });`
);
} else {
block.chunks.mount.push(
b`${resize_observer} = @add_resize_observer(${this.var}, ${callee}, { box: "${box}" });`
);
}
block.chunks.mount.push(
b`${resize_observer} = @add_resize_observer(${this.var}, ${callee}, { box: "${box}" });`
);


block.chunks.destroy.push(
b`${resize_observer}();`
);
} else {
block.event_listeners.push(
x`@listen(${this.var}, "${name}", ${callee})`
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/utils/patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ export const start_whitespace = /^[ \t\r\n]*/;
export const end_whitespace = /[ \t\r\n]*$/;

export const dimensions = /^(?:offset|client)(?:Width|Height)$/;

export const sizing_content_box = /^(?:contentRect|contentBoxSize)$/;
export const sizing_border_box = /^(?:borderBoxSize)$/;
export const sizing_device_pixel_content_box = /^(?:devicePixelContentBoxSize)$/;
10 changes: 9 additions & 1 deletion src/runtime/internal/dev.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { custom_event, append, append_hydration, insert, insert_hydration, detach, listen, attr } from './dom';
import { custom_event, add_resize_observer, append, append_hydration, insert, insert_hydration, detach, listen, attr } from './dom';
import { SvelteComponent } from './Component';

export function dispatch_dev<T=any>(type: string, detail?: T) {
Expand Down Expand Up @@ -89,6 +89,14 @@ export function set_data_dev(text, data) {
text.data = data;
}

export function add_resize_observer_dev(node: HTMLElement, fn: () => void, opts?: any) {
if ('ResizeObserver' in window) {
add_resize_observer(node, fn, opts);
} else {
throw new Error(`Sizing bindings uses the ResizeObserver API, but it is not supported in this browser.`);
}
}

export function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/internal/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,13 @@ export function add_resize_listener(node: HTMLElement, fn: () => void) {
};
}

export function add_resize_observer(node: HTMLElement, fn: () => void, opts?: any) {
// @ts-ignore ResizeObserver is not available in current tsconfig lib
const observer = new ResizeObserver(entries => fn.call(entries.values().next().value));
observer.observe(node, opts);
return () => observer.disconnect();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder if we could / should reuse the ResizeObserver instance

export function toggle_class(element, name, toggle) {
element.classList[toggle ? 'add' : 'remove'](name);
}
Expand Down