Skip to content

Add resize feature to Component #70

@marchant

Description

@marchant

When a component's element is resized as part of a layout / size changes around it, like when a window is resized, a mobile screen is rotated, or through being in an in-app Split View control, it may need to execute JavaScript code to relayout itself internally.

This is can be true for a slider that needs to reposition its thumbs/knobs, or a segmented control to position the highlight of the current selected segment.

We're going to do this by having the event manager host a single ResizeObserver for all components'element in a mod. This will be the single integration with the ResizeObserver API, and we'll dispatch from there as tested and validated performance-wise here (https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A , the last entry from Aleks Totic)

The most natural way to dispatch is to embrace events, and we're going to use the "change" event: ChangeEvent in core/event/change-event.js

That has started to be used in the data layer, starting in DataTrigger, however it's not using the EventManager there for dispatch, but we need to. ChangeEvent is not actually a subclass of Event or MutableEvent which is what we typically use, because it needs to work in the worker on top of node.js where DOM events don't exists. But as long as it behaves like an Event, it shouldn't matter to the EventManager.

There are 2 possible ways to go at this: hack the ResizeObserverEntry we're receiving via the ResizeObserver()'s callback to make it looks like and behave like a ChangeEvent, or create an actual ChangeEvent and move properties/values from the ResizeObserverEntry to it.

Those ResizeObserverEntry's properties are:

We need to find a home for those on the change event where the key property will be set to "size", We could just set the whole ResizeObserverEntry to the "keyValue" property of the change event:

var changeEvent = new ChangeEvent;
  changeEvent.target = aResizeObserverEntry.target;
  changeEvent.key = "size";
  changeEvent.keyValue = aResizeObserverEntry;
  this.handleEvent(changeEvent); // this here his the event manager, calling his own handleEvent() method from within the ResizeObserver's callback 

When a component want size changes events, it should use the options argument of addEventListener ( https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener ) to provide the fact that it needs to listen to changes on the "size" property, and also provide what the ResizeObserver: observe() method offers in term of options. An options object allowing you to set options for the observation, see more at https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options

Currently this only has one possible option that can be set, "box", with possible values being: content-box, border-box, device-pixel-content-box, so with a component it would look like this:

this.element.addEventListener(“change”, this, { 
    "size": {
        box: "device-pixel-content-box"
    }
});

per implementation in target.js, mod's addEventListener() ends up calling:
defaultEventManager.registerTargetEventListener(this, type, listener, optionsOrUseCapture);

So in EventManager's registerTargetEventListener(target, eventType, listener, optionsOrUseCapture) method, we need to add a new handling for a type "change" on a target that is an instance of Element or SVGElement, where we'll create on-demand EventManager's this._resizeObserver = new ResizeObserver(dispatchCallback) if it wasn't before, and use this._resizeObserver.observe(target, {box: optionsOrUseCapture.size.box}) to kickstart the observing.

dispatchCallback is the function where we get the changes and we'll have to turn it into an event and distribute them.

@tejaede @maxwell-jordan please take a look and comment as you see fit

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions