Initial rendering of a Todos-MVC app. (events are called but don't trigger a re-rendering yet, renderin-function does not yet support updating DOM yet)
This commit is contained in:
@@ -1,8 +1,22 @@
|
||||
import {render} from "../vdom";
|
||||
|
||||
/**
|
||||
* This CustomElement class is to avoid having to do an ugly workaround in every custom-element:
|
||||
* Which would be replacing 'HTMLElement' with '(class extends HTMLElement{})'
|
||||
*
|
||||
* Also, it is a good starting point for implementing render() functionality, listening to props, state changes, events and whatnot (use decorators)
|
||||
*/
|
||||
export class CustomElement extends HTMLElement {}
|
||||
export class CustomElement extends HTMLElement {
|
||||
connectedCallback() {
|
||||
if(this.render){
|
||||
let newVNode = this.render();
|
||||
render(newVNode, {
|
||||
host: this
|
||||
});
|
||||
}
|
||||
}
|
||||
disconnectedCallback(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,4 +15,7 @@ export const VNODEPROP_EXCLUDE_DIRECT = {
|
||||
|
||||
export const VNODEPROP_IGNORE = {
|
||||
['key']: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const Host = Symbol('host');
|
||||
export const ShadowDOM = Symbol('shadow-dom');
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./vnode";
|
||||
export * from "./render";
|
||||
export * from "./render";
|
||||
export {Host, ShadowDOM} from "./constants";
|
||||
@@ -1,5 +1,6 @@
|
||||
import './vnode';
|
||||
import {
|
||||
Host, ShadowDOM,
|
||||
VNODE_EXCLUDE,
|
||||
VNODEPROP_DIRECT, VNODEPROP_EXCLUDE_DIRECT,
|
||||
VNODEPROP_IGNORE,
|
||||
@@ -21,7 +22,11 @@ import {
|
||||
* @return {Element}
|
||||
*/
|
||||
export function render(vnode, opts = {}) {
|
||||
// Replace how this works into a queue instead of a recursive call, also consider changing JSX to support (changed)="..." notation
|
||||
// TODO figure out how to handle events (its not that easy to create (click)={this.onClick} or something, that is not supported by the @babel/parser and we'd have to fork it..
|
||||
// ---> We've got a basic onClick (react-style) system set up now
|
||||
// TODO innerHTML, innerText and other tags/props that are trickyer then just mapping value to attribute
|
||||
// TODO: Replace how this works into a queue instead of a recursive call, also consider changing JSX to support (changed)="..." notation
|
||||
// TODO ref-prop (should it only return once all child els are created and appended to the child?!)
|
||||
let {
|
||||
/**
|
||||
* @type {Element}
|
||||
@@ -30,61 +35,84 @@ export function render(vnode, opts = {}) {
|
||||
} = opts;
|
||||
|
||||
if(VNODE_EXCLUDE[vnode]) return undefined;
|
||||
console.log(vnode);
|
||||
|
||||
if(vnode instanceof Object){
|
||||
// Type
|
||||
let tagName = vnode.type instanceof Object? vnode.type.tagName : vnode.type;
|
||||
if(!host) host = document.createElement(tagName);
|
||||
|
||||
// Props
|
||||
if (vnode.props) {
|
||||
if (vnode.props.style && typeof (vnode.props.style) === 'object') {
|
||||
for (let styleKey in vnode.props.style) {
|
||||
host.style[ styleKey ] = vnode.props.style[ styleKey ];
|
||||
}
|
||||
if(!host){
|
||||
if(!['object', 'function', 'symbol'].includes(typeof(vnode))){
|
||||
host = document.createTextNode(vnode);
|
||||
}else if(typeof(vnode?.type) === 'string'){
|
||||
host = document.createElement(vnode.type);
|
||||
}else if(vnode?.type?.tagName){
|
||||
host = document.createElement(vnode.type.tagName);
|
||||
}else{
|
||||
throw new Error("Unrecognized vnode type", vnode);
|
||||
}
|
||||
}
|
||||
|
||||
// Props
|
||||
if (vnode?.props) {
|
||||
if (vnode.props.style && typeof (vnode.props.style) === 'object') {
|
||||
for (let styleKey in vnode.props.style) {
|
||||
host.style[ styleKey ] = vnode.props.style[ styleKey ];
|
||||
}
|
||||
for (let key in vnode.props) {
|
||||
let val = vnode.props[key];
|
||||
if(VNODEPROP_IGNORE[key]){
|
||||
// NO-OP
|
||||
}else if(VNODEPROP_DIRECT[key]){
|
||||
}
|
||||
for (let key in vnode.props) {
|
||||
let val = vnode.props[key];
|
||||
if(VNODEPROP_IGNORE[key]){
|
||||
// NO-OP
|
||||
}else if(VNODEPROP_DIRECT[key]){
|
||||
host[key] = val;
|
||||
}else{
|
||||
if(!VNODEPROP_EXCLUDE_DIRECT[key] && !key.indexOf('-')){
|
||||
host[key] = val;
|
||||
}else{
|
||||
if(!VNODEPROP_EXCLUDE_DIRECT[key] && !key.indexOf('-')){
|
||||
host[key] = val;
|
||||
}
|
||||
if(key.slice(0,2)==='on' && key[2]>='A' && key[2]<='Z'){
|
||||
if(val instanceof Function){
|
||||
host.addEventListener(
|
||||
// Convert camelCase to dash-case
|
||||
key[2].toLowerCase()+key.slice(3).replace(/[A-Z]/g, function(c){return('-'+c.toLowerCase())}),
|
||||
val
|
||||
);
|
||||
}else{
|
||||
new Error("Unsupported event-handler");
|
||||
}
|
||||
if (val === false) {
|
||||
|
||||
}else {
|
||||
if (val === false || val===null || val==='') {
|
||||
host.removeAttribute(key);
|
||||
} else if (val === true) {
|
||||
host.setAttribute(key, "");
|
||||
} else{
|
||||
} else {
|
||||
host.setAttribute(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Children
|
||||
if (vnode.children) {
|
||||
let children = vnode.children instanceof Array? vnode.children : [vnode.children];
|
||||
|
||||
for(let child of children){
|
||||
let el = child instanceof Element? child : render(child, {
|
||||
...opts,
|
||||
host: undefined
|
||||
});
|
||||
if(el!==undefined){
|
||||
host.appendChild(el);
|
||||
// Children
|
||||
if (vnode?.children) {
|
||||
let queue = vnode.children instanceof Array? vnode.children.slice() : [vnode.children];
|
||||
while(queue.length){
|
||||
let child = queue.splice(0,1)[0];
|
||||
if(child instanceof Array){
|
||||
queue.splice(0,0,...child);
|
||||
}else{
|
||||
if(child?.type === ShadowDOM){
|
||||
let shadow = host.attachShadow({mode: 'open'});
|
||||
render({children: child.children}, {
|
||||
...opts,
|
||||
host: shadow
|
||||
});
|
||||
}else{
|
||||
let el = child instanceof Element? child : render(child, {
|
||||
...opts,
|
||||
host: undefined
|
||||
});
|
||||
if(el!==undefined) host.appendChild(el);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TODO figure out how to handle events (its not that easy to create (click)={this.onClick} or something, that is not supporter by the @babel/parser and we'd have to fork it..
|
||||
// TODO ref-prop (should it only return once all child els are created and appended to the child?!)
|
||||
// TODO innerHTML, innerText and other tags/props that are trickyer then just mapping value to attribute
|
||||
}else{
|
||||
if(!host) host = document.createTextNode(vnode);
|
||||
}
|
||||
|
||||
return host;
|
||||
|
||||
Reference in New Issue
Block a user