2
0
mirror of https://github.com/esiur/iui.git synced 2025-06-27 17:33:12 +00:00
This commit is contained in:
2021-11-06 14:53:52 +03:00
parent 1bac10e60d
commit 589c4f3227
31 changed files with 12721 additions and 315 deletions

View File

@ -1,9 +1,11 @@
import IUIElement from "../Core/IUIElement.js";
import { IUI } from "../Core/IUI.js";
import RefsCollection from "./RefsCollection.js";
export default IUI.module(class App extends IUIElement {
constructor() {
super();
this.refs = new RefsCollection(this);
}
create() {
@ -11,8 +13,14 @@ export default IUI.module(class App extends IUIElement {
window.app = this;
}
created() {
IUI.bind(this, this, "/");
IUI.bind(this, this, "/", {app: this, refs: this.refs});
// update referencing
this.refs._build();
//IUIElement._make_bindings(this);
this.render();
this._emit("load", { app: this });

View File

@ -20,7 +20,7 @@ export const AttributeBindingDestination = {
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
export class Binding {
static create(nodeOrAttributeOrIUIElement) {
static create(nodeOrAttributeOrIUIElement, scope) {
var code, isAsync, type, attrType, attrKey, func, script;
//if (nodeOrAttributeOrIUIElement.created)
@ -100,11 +100,17 @@ export class Binding {
// test the function
let scopeKeys = Object.keys(scope);
let scopeValues = Object.values(scope);
try {
let args = ["data", "d", "context", "_test",
...scopeKeys]
if (isAsync)
func = new AsyncFunction("data", "d", "context", "_test", code);
func = new AsyncFunction(...args, code);
else
func = new Function("data", "d", "context", "_test", code);
func = new Function(...args, code);
}
catch (ex) {
console.log("Test failed: " + ex, code);
@ -113,7 +119,7 @@ export class Binding {
let rt = new Binding();
Object.assign(rt, { isAsync, type, attrType, attrKey, func, target: nodeOrAttributeOrIUIElement, checked: false, script });
Object.assign(rt, { isAsync, type, attrType, attrKey, func, target: nodeOrAttributeOrIUIElement, checked: false, script, scopeKeys, scopeValues });
return rt;
}
@ -144,7 +150,9 @@ export class Binding {
let proxy = new Proxy(map, detector);
try {
let d = this.func.apply(thisArg, [proxy, proxy, {}, true]);
let d = this.func.apply(thisArg, [proxy, proxy, {}, true
, ...this.scopeKeys]);
this.map = map;
return d;
}
@ -158,9 +166,9 @@ export class Binding {
if (!this.checked)
this._findMap(thisArg);
let context = {};
var rt = this.func.apply(thisArg, [data, data, context, false]);
var rt = this.func.apply(thisArg, [data, data, context, false,
...this.scopeValues]);
//console.log(rt);
if (rt instanceof Promise)
@ -231,9 +239,12 @@ export class Binding {
try {
if (this.type === BindingType.IUIElement) {
let d = this.func.apply(this.target, [data, data]);
if (d instanceof Promise)
d = await d;
//let d = this.func.apply(this.target, [data, data]);
//if (d instanceof Promise)
// d = await d;
let d = await this._execute(this.target, data);
await this.target.setData(d);
}
else if (this.type === BindingType.TextNode) {
@ -277,8 +288,10 @@ export class Binding {
if (window?.app?.loaded)
{
await IUI.create(targetElement);
IUI.bind(targetElement, true, "content", targetElement.__i_bindings?.scope);
// update references
targetElement.__i_bindings?.scope?.refs?._build();
await IUI.created(targetElement);
IUI.bind(targetElement, targetElement, "content");
await IUI.render(targetElement, targetElement._data, true);
}
//await IUI.updateTree(targetElement);

21
src/Core/BindingList.js Normal file
View File

@ -0,0 +1,21 @@
export default class BindingList extends Array {
constructor(target, scope) {
super();
this.target = target;
this.scope = scope;
}
getArgumentsNames(){
if (this.scope == null)
return [];
let rt;
for (var i in this.scope.length)
rt.push(i);
return rt;
}
}

View File

@ -1,6 +1,7 @@
import IUIElement from "./IUIElement.js";
import { Binding, BindingType } from "./Binding.js";
//import Route from '../Router/Route.js';
import BindingList from "./BindingList.js";
export class IUI {
@ -124,13 +125,13 @@ export class IUI {
return objectClass;
}
static extend(properties, defaults, force)
static extend(properties, defaults, overwrite)
{
if (properties == null)
properties = defaults;
else
for(var i in defaults)
if (force)
if (overwrite)
properties[i] = defaults[i];
else if (properties[i] === undefined)
properties[i] = defaults[i];
@ -138,38 +139,55 @@ export class IUI {
}
static bind(element, rootElement, sourcePath){
static bind(element, skipAttributes, sourcePath, scope) {
// ::Attribute
// : Field
// : Field
// async:: Async Attribute
// async: Async Field
// @ Event
// skip element ?
if (element.hasAttribute("skip")
|| element.hasAttribute("i-skip")
|| element instanceof HTMLTemplateElement)
return;
// tags to skip
//if (element instanceof HTMLScriptElement )
//return;
if (rootElement == null)
rootElement = element;
let bindings;
if (element != rootElement)
{
element.view = rootElement.view;
element.route = rootElement.route;
if (scope == null)
scope = {};
// get refs before they get overwritten
//let refs = scope?.refs;
// some element extended or overwritten the binding arguments
if (element.scope != null)
IUI.extend(scope, element.scope, true);
bindings = new BindingList(element, scope);
if (skipAttributes)
{
// copy attributes bindings
if (element.__i_bindings != null)
for(var i = 0; i < element.__i_bindings.length; i++)
if (element.__i_bindings[i].type != BindingType.TextNode)
bindings.push(element.__i_bindings[i]);
}
else
{
bindings = [];
// compile attributes
for (var i = 0; i < element.attributes.length; i++) {
let b = Binding.create(element.attributes[i]);
let b = Binding.create(element.attributes[i],
bindings.scope);
if (b != null) {
if (b.type == BindingType.HTMLElementDataAttribute
@ -182,37 +200,50 @@ export class IUI {
}
}
// add reference
if (element.hasAttribute("ref")) {
rootElement.refs[el.getAttribute("ref")] = element;
}
// if (element.hasAttribute("ref")) {
// let ref = element.getAttribute("ref");
// if (refs[ref] == null)
// refs[ref] = element;
// else if (refs[ref] == element){
// // do nothing
// }
// else if (refs[ref] instanceof Array){
// refs[ref].push(element);
// } else {
// var firstRef = refs[ref];
// refs[ref] =[firstRef, element];
// }
// }
}
else
{
// remove previous text node bindings
bindings = element.bindings == null ? [] : element.bindings.filter(x=> x.type != BindingType.TextNode);
element.refs = {};
}
// get new refs (scope might been overwritten)
//refs = scope?.refs;
// compile nodes
for (var i = 0; i < element.childNodes.length; i++) {
let el = element.childNodes[i];
if (el instanceof IUIElement) {
// @TODO: check if the IUI element handles the binding
IUI.bind(el, rootElement, sourcePath);
IUI.bind(el, false, sourcePath, scope);
}
else if (el instanceof HTMLElement) {
IUI.bind(el, rootElement, sourcePath);
IUI.bind(el, false, sourcePath, scope);
}
else if (el instanceof Text) {
let b = Binding.create(el);
let b = Binding.create(el, bindings.scope);
if (b != null)
bindings.push(b);
}
else if (el instanceof HTMLScriptElement)
{
// this because HTML parser don't evaluate script tag
let func = new Function("//# sourceURL=iui://" + sourcePath + "-" + Math.round(Math.random() * 10000) + "\r\n return " + el.text.trim());
/// let func = new Function("//# sourceURL=iui://" + sourcePath + "-" + Math.round(Math.random() * 10000) + "\r\n return " + el.text.trim());
let func = new Function("//# sourceURL=iui://" + sourcePath + "-" + Math.round(Math.random() * 10000) + "\r\n" + el.text.trim());
let rt = func.call(el.parentElement);
if (typeof (rt) === "object") {
@ -222,24 +253,25 @@ export class IUI {
}
}
element.bindings = bindings;
element.__i_bindings = bindings;
}
static async render(element, data, textNodesOnly = false) {
if (!element.bindings) {
if (!element.__i_bindings) {
return;
}
let bindings = element.__i_bindings;
if (textNodesOnly) {
for (var i = 0; i < element.bindings.length; i++)
if (element.bindings[i].type == BindingType.TextNode)
await element.bindings[i].render(data);
for (var i = 0; i < bindings.length; i++)
if (bindings[i].type == BindingType.TextNode)
await bindings[i].render(data);
} else {
// render attributes & text nodes
for (var i = 0; i < element.bindings.length; i++)
await element.bindings[i].render(data);
for (var i = 0; i < bindings.length; i++)
await bindings[i].render(data);
}
// render children

View File

@ -41,7 +41,6 @@ export default class IUIElement extends HTMLElement {
async render() {
await IUI.render(this, this._data);
//await IUIElement._renderElement(this, this._data);
}
_getParentData() {
@ -57,22 +56,15 @@ export default class IUIElement extends HTMLElement {
async setData(value) {
this._data = value;
this._emit("data", {data: value});
await IUIElement._renderElement(this, value);
await IUI.render(this, value);
}
get data() {
return this._data;
}
async revert(){
//if (this.revertMap != null)
//{
//if (data == undefined)
// await this.revertMap.render(this._getParentData());
//else
// await this.revertMap.render(data);
// revert parents
let e = this;
do {
@ -81,7 +73,6 @@ export default class IUIElement extends HTMLElement {
if (e.revertMap != null)
await e.revertMap.render(p?.data);
} while (e = p);
//}
}
async update(data) {
@ -101,51 +92,17 @@ export default class IUIElement extends HTMLElement {
}
}
static async _renderElement(element, data) {
if (!element.bindings) {
return;
}
// render attributes & text nodes
for (var i = 0; i < element.bindings.length; i++)
await element.bindings[i].render(data);
// render children
for (var i = 0; i < element.children.length; i++) {
let e = element.children[i];
if (e instanceof IUIElement)
// @TODO should check if the element depends on parent or not
if (e.dataMap != null) {
// if map function failed to call setData, we will render without it
if (!(await e.dataMap.render(data)))
await e.render();
}
else
await e.setData(data);
else {
if (e.dataMap != null)
await e.dataMap.render(data);
else
e.data = data;
//let data = e.mapData(data);
await IUIElement._renderElement(e, e.data);
}
}
// bindings arguments
get scope(){
return null;
}
// this meant to be inherited
modified() {
}
get data() {
return this._data;
}
connectedCallback() {
if (this.hasAttribute("css-class"))
{
this.classList.add(this.getAttribute("css-class"));
@ -182,8 +139,6 @@ export default class IUIElement extends HTMLElement {
}
destroy() {
console.log("Destroy", this);
IUI.registry.splice(IUI.registry.indexOf(this), 1);
if (this.parentNode)
this.parentNode.removeChild(this);
@ -191,67 +146,6 @@ export default class IUIElement extends HTMLElement {
static _make_bindings(element, isRoot = false) {
// ::Attribute
// : Field
// async:: Async Attribute
// async: Async Field
// @ Event
// skip element ?
if (element.hasAttribute("skip"))
return;
// tags to skip
if (element instanceof HTMLScriptElement
|| element instanceof HTMLTemplateElement)
return;
let bindings = [];
if (!isRoot)
{
// compile attributes
for (var i = 0; i < element.attributes.length; i++) {
let b = Binding.create(element.attributes[i]);
if (b != null) {
if (b.type == BindingType.HTMLElementDataAttribute
|| b.type == BindingType.IUIElementDataAttribute)
element.dataMap = b;
else if (b.type == BindingType.RevertAttribute)
element.revertMap = b;
else
bindings.push(b);
}
}
}
// compile nodes
for (var i = 0; i < element.childNodes.length; i++) {
let e = element.childNodes[i];
if (e instanceof IUIElement) {
// @TODO: check if the IUI element handles the binding
IUIElement._make_bindings(e);
}
else if (e instanceof HTMLElement) {
IUIElement._make_bindings(e);
}
else if (e instanceof Text) {
let b = Binding.create(e);
if (b != null)
bindings.push(b);
}
}
element.bindings = bindings;
}
_emit(event, values) {
//var args = Array.prototype.slice.call(arguments, 1);
var e = new CustomEvent(event, values);
@ -304,13 +198,13 @@ export default class IUIElement extends HTMLElement {
}
}
off(event, fn) {
this.removeEventListener(event, fn);
off(event, func) {
this.removeEventListener(event, func);
return this;
}
on(event, fn) {
this.addEventListener(event, fn, false);
on(event, func) {
this.addEventListener(event, func, false);
return this;
}
}

View File

@ -0,0 +1,46 @@
export default class RefsCollection
{
constructor(rootElement){
this._rootElement = rootElement;
}
_build(element) {
if (element == undefined)
element = this._rootElement;
for(var i in this)
if (i != "_rootElement" && i != "_build")
delete this[i];
for(var i = 0; i < element.children.length; i++)
{
let child = element.children[i];
if (child.hasAttribute("ref"))
{
let ref = child.getAttribute("ref");
if (this[ref] == null)
this[ref] = child;
else if (this[ref] == child){
// do nothing
}
else if (this[ref] instanceof Array){
this[ref].push(child);
} else {
var firstRef = this[ref];
this[ref] =[firstRef, child];
}
}
if (child.refs != undefined)
// opt out if the element handles referencing
break;
else
this._build(child);
}
}
}