-
Notifications
You must be signed in to change notification settings - Fork 37
Property Applicators for VNodes #811
Description
Enhancement
The meta functionality has evolved from its initial concept of being a mechanism that provides access to simply get information from a DOM node to being used more generally for both reading information and also writing/adding information to DOM nodes created by the virtual DOM system.
For reading information, such as Dimensions and IntersectionObserver using this.meta during the render lifecycle seems to provides adequate ergonomics and functionality.
render() {
const inView = this.meta(IntersectionObserver).get('root');
const properties = inView ? { src: 'image/src' } : {};
return v('img', properties);
}However, when used to "set" properties or "add" functionality to a DOM node it feels clunky to need to do this in a decoupled programmatic fashion and not declaratively when using v() for the required node.
By adding a concept of an applicator, it will enable declaring meta functionality directly on the node(s) that they are needed.
render() {
return v('div', {
focus: meta(Focus, true)
});
}The VDom applicators are not limited to just metas, but will enable consumers to write other extended functionality to hook into the vdom rendering system, for example, custom application (or diffing strategy) for a specific attribute or property.
// Only adds classes to the Node (example only)
function classes(classes: string[]) {
return {
apply(domNode: Element, previousProperties: VNodeProperties, properties: VNodeProperties) {
domNode.classList.add(classes);
}
};
}
// usage
render() {
return v('div', {
classes: classes([ 'classOne', 'classTwo', this.them(themeClassOne) ])
});
}Another possible use-case would be to support specific bags for properties, attributes, with custom applicators:
// example attributes implementation
function attributes(...attrs: { [index: string]: string }[]) {
return {
apply(domNode: Element, previousProperties: VNodeProperties, properties: VNodeProperties) {
Object.keys(properties).forEach((propertyKey) => {
if (previousProperties[propertyKey] !== properties[propertyKey]) {
domNode.setAttribute(propertyKey, properties[propertyKey]);
}
});
}
};
}
// example properties implementation
function properties(...props: { [index: string]: any }[]) {
return {
apply(domNode: Element, previousProperties: VNodeProperties, properties: VNodeProperties) {
Object.keys(properties).forEach((propertyKey) => {
if (previousProperties[propertyKey] !== properties[propertyKey]) {
domNode[propertyKey] = properties[propertyKey];
}
});
}
};
}
// usage
render() {
return v('div', {
attributes: attributes({ href: 'href' }),
properties: properties({ 'some-properties': { foo: 'bar' } })
});
}It would be nice to be able to restrict the key used by an applicator implementation, but this could prove challenging, if possible at all.