As you know https://github.com/skatejs/skatejs 5.x is using Preact as default renderer ( we switched from Incremental DOM )
What would be awesome, is to add support for resolving custom-elements constructors within h so we can provide functionality like in v4:
import {
h, // h is re-exporeted Preacts h
Component // skate WebComponent composed mixin
} from 'skate'
import { MyUserCard } from './web-components'
import { store } from './store'
class MyCmp extends Component {
static get is(){ return 'my-cmp' }
connectedCallback(){
this.data = store.get('user')
}
renderCallback(){
return (
<div>
<h1>Hello World</h1>
<MyUserCard user={this.data} />
</div>
)
}
}
window.customElements.define( MyCmp.is, MyCmp )
so <MyUserCard user={this.data} /> will be recognised by h that it's a custom element and will output -> <my-user-card></my-user-card>
This feature would also add better support to raw WebComponents which as win win for everyone #useThePlatform
@Hotell I am trying to figure out how Preact could actually resolve this itself - how would it know the given function isn't a Component or pure functional Component?
I can't remember if I've suggested it before, but would it be feasible to do something like this in skate?
There'd be two options there - the first is to provide that functionality via your re-exported h(), instead exporting a wrapper:
import { createElement } from 'preact';
// (in 8.x createElement is an alias of h, using it here to avoid name conflict)
export function h(nodeName, ...args) {
if (typeof nodeName==='function' && typeof nodeName.is==='string') {
nodeName = nodeName.is;
}
return createElement(nodeName, ...args);
}
Option 2 would be to use preact's hooks to inject the Web Component constructor swapping into anything using Preact, even non-skate things:
import { options } from 'preact';
let oldHook = options.vnode;
options.vnode = vnode => {
// this is super cheap because it mutates VNodes in-place and doesn't change shape
if (typeof vnode.nodeName==='function' && typeof vnode.nodeName.is==='string') {
vnode.nodeName = vnode.nodeName.is;
}
if (oldHook) oldHook(vnode);
}
both solutions look good although that one where you check for is may be a weak assumption
@Hotell I am trying to figure out how Preact could actually resolve this itself - how would it know the given function isn't a Component or pure functional Component?
checking prototype of the element I guess ?
if (Object.getPrototypeOf(nodeName) === HTMLElement) {
return new nodeName();
}
cc @treshugart
I messed with this a bit today and got it working at the diff level while only changing a few lines of code. Problem is, we need the new Babel because the version Preact is currently on doesn't transpile classes correctly to extend natives. Babel is being upgraded in #683 and I have all the code ready to go for this change.
Sweeeeeeet Christmassss !
@developit see PR, but re:
I am trying to figure out how Preact could actually resolve this itself - how would it know the given function isn't a Component or pure functional Component?
Basically vnodeName.prototype instanceof HTMLElement in the typeof vnodeName === 'function' check.
Custom Elements also inherit from Node so vnodeName.ELEMENT_NODE === 1 should also work since HTMLElement is not available in the global namespace of ex. Node.js so it might throw when using the renderToString module.
Is HTMLElement the only class Custom Elements can extend?
Custom Elements also inherit from Node so vnodeName.ELEMENT_NODE === 1 should also work since HTMLElement is not available in the global namespace of ex. Node.js so it might throw when using the renderToString module.
This would also be a good approach. Happy to explore that option.
Is HTMLElement the only class Custom Elements can extend?
It's the furthest up it can extend. Custom elements can extend other custom elements, though.