scope
9072
build/iui.js
Normal file
@ -7,20 +7,19 @@
|
||||
<meta charset="UTF-8" content="">
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
|
||||
<link rel="manifest" href="site.webmanifest">
|
||||
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#5bbad5">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="site/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="site/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="site/favicon-16x16.png">
|
||||
<link rel="manifest" href="site/site.webmanifest">
|
||||
<link rel="mask-icon" href="site/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#ffc40d">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
|
||||
|
||||
<!--IUI 2.0 -->
|
||||
<link href="../css/iui.css" rel="stylesheet" />
|
||||
@ -86,6 +85,10 @@
|
||||
|
||||
<i-route name="declarative" caption="Declarative Rendering" :content="load(this)">
|
||||
</i-route>
|
||||
|
||||
<i-route name="scope" caption="Scope" :content="load(this)">
|
||||
</i-route>
|
||||
|
||||
<i-route name="data-flow" caption="Data Flow" :content="load(this)">
|
||||
</i-route>
|
||||
|
||||
@ -96,13 +99,14 @@
|
||||
|
||||
|
||||
|
||||
<i-route name="IUI Widgets" caption="IUI Widgets">
|
||||
<i-route name="components" caption="Components">
|
||||
|
||||
<i-route name="IUIElement" caption="IUIElement">
|
||||
|
||||
<i-route name="iui-element" caption="IUIElement" :content="load(this)">
|
||||
</i-route>
|
||||
|
||||
<i-route name="repeat" caption="Repeat" :content="load(this)">
|
||||
</i-route>
|
||||
|
||||
<i-index></i-index>
|
||||
</i-route>
|
||||
|
||||
|
||||
|
@ -11,6 +11,6 @@ async function load(route, skip=false) {
|
||||
let content = await fetch(link);
|
||||
if (content.status != 200)
|
||||
return "Not found " + content.url;
|
||||
var md = marked(await content.text());
|
||||
var md = marked.parse(await content.text());
|
||||
return skip ? "<div skip>" + md + "</div>" : md;
|
||||
}
|
24
docs/md/components/iui-element.md
Normal file
@ -0,0 +1,24 @@
|
||||
# IUI Element
|
||||
|
||||
*IUIElement* is the base class for all IUI components, it extends the HTMLElement, adds more functionality and define itself as a customElement in the browser.
|
||||
|
||||
IuI Widgets tags start with `<i-...>` to differentiate them from the built in html tags.
|
||||
|
||||
# Initialization Process
|
||||
|
||||
When the document is loaded or new HTML is inserted in the document, IUI process of initialization start which consists of
|
||||
|
||||
1- IUI.create(rootElement)
|
||||
create is called on every IUI element in the tree, asking these elements to create themselves, in this phase an element can add more children to itself. (**IUIElement**)
|
||||
|
||||
1- IUI.bind(rootElement)
|
||||
IUI.bind ensures that all executable attributes starting with : are compiled and tested for data binding.
|
||||
Any property that is used by the `d` variable will be a candidate for binding, which means IUI will handle any modification on that property. (**IUIElement** and **HTMLElement**)
|
||||
|
||||
3- IUI.created(rootElement)
|
||||
Notify every IUI element that the create and bind process has ended. (**IUIElement**)
|
||||
|
||||
4- IUI.render(element)
|
||||
Render invokes the data binding functions and sets the element text nodes and attributes to the value returned by the binding functions. The process is recursive to all children in the element tree (**IUIElement** and **HTMLElement**)
|
||||
|
||||
The rootElement is the first `<i-app>` in the document when its loaded.
|
14
docs/md/components/repeat.md
Normal file
@ -0,0 +1,14 @@
|
||||
# I-Repeat
|
||||
|
||||
`<i-repeat>` performs a an iteration on an array and builds elements according to the given list
|
||||
|
||||
|
||||
<i-codepreview>
|
||||
<i-repeat :data="['Apple', 'Orange', 'Grape']">
|
||||
<div>${d}</div>
|
||||
</i-repeat>
|
||||
</i-codepreview>
|
||||
|
||||
if the iteration on a specific element is needed, *repeat* attribute must be added to the element.
|
||||
|
||||
*Note* each element in the iteration has an index property
|
13
docs/md/getting-started/scope.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Scope Variables
|
||||
|
||||
Scope variables are provided during the rendering process to the scripts and declarative rendering functions.
|
||||
|
||||
These variables propagate from an element to its children and could be altered or overwritten by any element in the tree so the children of that element receive a new value for these variables.
|
||||
|
||||
|
||||
<i-codepreview>
|
||||
<div :scope="{now: new Date()}">
|
||||
<div>Date: ${now.toDateString()}</div>
|
||||
<div>Time: ${now.toTimeString()}</div>
|
||||
</div>
|
||||
</i-codepreview>
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 546 B After Width: | Height: | Size: 546 B |
Before Width: | Height: | Size: 871 B After Width: | Height: | Size: 871 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
3322
package-lock.json
generated
Normal file
10
package.json
@ -8,8 +8,16 @@
|
||||
"test": "test"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.16.0",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/preset-env": "^7.16.0",
|
||||
"babelify": "^10.0.0",
|
||||
"browserify": "^17.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "browserify src/iui.js -t babelify --outfile build/iui.js",
|
||||
"prepublish": "browserify src/iui.js -t babelify --outfile build/iui.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -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 });
|
||||
|
@ -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
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
102
src/Core/IUI.js
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
46
src/Core/RefsCollection.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import IUIElement from "../Core/IUIElement.js";
|
||||
import { IUI } from "../Core/IUI.js";
|
||||
import RefsCollection from "../Core/RefsCollection.js";
|
||||
|
||||
export default IUI.module(class Include extends IUIElement
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.refs = {};
|
||||
this.refs = new RefsCollection();
|
||||
}
|
||||
|
||||
get src(){
|
||||
@ -18,6 +19,11 @@ export default IUI.module(class Include extends IUIElement
|
||||
this._load(value);
|
||||
}
|
||||
|
||||
|
||||
get scope() {
|
||||
return {view: this, refs: this.refs};
|
||||
}
|
||||
|
||||
async _load(url)
|
||||
{
|
||||
if (this._loading)
|
||||
@ -42,8 +48,11 @@ export default IUI.module(class Include extends IUIElement
|
||||
if (window?.app?.loaded)
|
||||
{
|
||||
await IUI.create(this);
|
||||
IUI.bind(this, true, "include:" + src,
|
||||
IUI.extend(this._i__bindings.scope, this.scope, true));
|
||||
|
||||
this.refs._build();
|
||||
await IUI.created(this);
|
||||
IUI.bind(this, this, "include:" + src);
|
||||
await IUI.render(this, this._data, true);
|
||||
}
|
||||
|
||||
@ -99,4 +108,8 @@ export default IUI.module(class Include extends IUIElement
|
||||
await this._load(this.getAttribute("src"));
|
||||
}
|
||||
|
||||
async created() {
|
||||
this.refs._build();
|
||||
}
|
||||
|
||||
});
|
@ -22,7 +22,7 @@ export default IUI.module(class Repeat extends IUIElement
|
||||
//////////////
|
||||
/// Create ///
|
||||
//////////////
|
||||
|
||||
|
||||
if (this._created)
|
||||
debugger;
|
||||
|
||||
@ -53,11 +53,11 @@ export default IUI.module(class Repeat extends IUIElement
|
||||
}
|
||||
|
||||
|
||||
var newElements = this.querySelectorAll("*");
|
||||
for (var i = 0; i < newElements.length; i++)
|
||||
newElements[i].repeat = this;
|
||||
// var newElements = this.querySelectorAll("*");
|
||||
// for (var i = 0; i < newElements.length; i++)
|
||||
// newElements[i].repeat = this;
|
||||
|
||||
var self = this;
|
||||
// var self = this;
|
||||
|
||||
/*
|
||||
this._repeatModified = function(propertyName, value)
|
||||
@ -104,31 +104,6 @@ export default IUI.module(class Repeat extends IUIElement
|
||||
return this._data.length;
|
||||
}
|
||||
|
||||
_assign(node, index) {
|
||||
|
||||
// update fields
|
||||
|
||||
// this so we won't mess with i-include view
|
||||
if (node.view == undefined)
|
||||
node.view = this.view;
|
||||
|
||||
node.rotue = this.route;
|
||||
node.index = index;
|
||||
|
||||
// update references
|
||||
if (node.hasAttribute("ref"))
|
||||
{
|
||||
let ref = node.getAttribute("ref");
|
||||
// create new array
|
||||
if (!(this.view.refs[ref] instanceof Array))
|
||||
this.view.refs[ref] = [];
|
||||
this.view.refs[ref][index] = node;
|
||||
}
|
||||
|
||||
//Object.assign(node, customFields);
|
||||
for (var i = 0; i < node.children.length; i++)
|
||||
this._assign(node.children[i], index);
|
||||
}
|
||||
|
||||
async setData(value)
|
||||
{
|
||||
@ -141,18 +116,14 @@ export default IUI.module(class Repeat extends IUIElement
|
||||
return false;
|
||||
}
|
||||
|
||||
//console.log("RPT: SetData", value);
|
||||
this._busy = true;
|
||||
|
||||
// var id = Math.random();
|
||||
|
||||
//console.log("SetData " + this.getAttribute("ref") + " " + id, value);
|
||||
//console.trace();
|
||||
|
||||
// clear
|
||||
this.clear();
|
||||
|
||||
if (value instanceof Structure)
|
||||
value = value.toPairs();
|
||||
if (value?.toArray instanceof Function)
|
||||
value = value.toArray();
|
||||
else if (value == null || !(value instanceof Array || value instanceof Int32Array))
|
||||
value = [];
|
||||
|
||||
@ -163,100 +134,24 @@ export default IUI.module(class Repeat extends IUIElement
|
||||
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
|
||||
///console.log("ST1");
|
||||
//let content = this.template.content.cloneNode(true);
|
||||
//let nodes = content.childNodes;
|
||||
|
||||
let e = this._repeatNode.cloneNode(true);
|
||||
|
||||
this.list.push(e);
|
||||
|
||||
await IUI.create(e);
|
||||
|
||||
//console.log("ST2");
|
||||
|
||||
|
||||
// Create node
|
||||
if (e instanceof IUIElement)
|
||||
await e.create();
|
||||
IUI.bind(e, false, "repeat",
|
||||
IUI.extend(this.__i_bindings?.scope,
|
||||
{index: i, repeat: this}, true));
|
||||
|
||||
// console.log("ST3");
|
||||
// Create children
|
||||
//console.log("Create repeat " + i, this, e);
|
||||
|
||||
await IUI.create(e);
|
||||
|
||||
//console.log("Created repeat " + i, this, e);
|
||||
|
||||
|
||||
//console.log("ST4");
|
||||
|
||||
//this._make_bindings(e)
|
||||
IUI.bind(e, this, "repeat");
|
||||
this._container.insertBefore(e, this._beforeNode);
|
||||
|
||||
// update referencing
|
||||
this.__i_bindings?.scope?.refs?._build();
|
||||
|
||||
this._assign(e, i);// { view: this.view, route: this.route, index: i });
|
||||
|
||||
|
||||
//console.log("ST5");
|
||||
|
||||
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(value[i])))
|
||||
await e.render();
|
||||
}
|
||||
else{
|
||||
await e.setData(value[i]);
|
||||
// console.log("ST6.1");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (e.dataMap != null)
|
||||
await e.dataMap.render(value[i]);
|
||||
else
|
||||
e.data = value[i];
|
||||
|
||||
// console.log("ST6.2", e);
|
||||
await this._renderElement(e, e.data);
|
||||
// console.log("ST6.3");
|
||||
}
|
||||
|
||||
// if (node.dataMap != null) {
|
||||
// await node.dataMap.render(value[i]);
|
||||
// this._renderElement(node, node.data);
|
||||
// }
|
||||
// else {
|
||||
|
||||
// node.data = value[i];
|
||||
// this._renderElement(node, node.data);
|
||||
// }
|
||||
|
||||
/*
|
||||
|
||||
var newElements = content.querySelectorAll("*");
|
||||
|
||||
while (nodes.length > 0) {
|
||||
let n = nodes[0];
|
||||
//n.index = i;
|
||||
if (n instanceof HTMLElement)
|
||||
n.setAttribute(":data", `d[${i}]`);
|
||||
this._container.appendChild(n);
|
||||
}
|
||||
|
||||
// this has to be called after appending the child otherwise node will be HTMLElement and not IUIElement , bug maybe in webkit ?
|
||||
for (var j = 0; j < newElements.length; j++) {
|
||||
let el = newElements[j];
|
||||
|
||||
// set route for all elements
|
||||
el.index = i;
|
||||
el.view = this;
|
||||
el.route = this.route;
|
||||
//newElements[j].route = this.route;
|
||||
if (el instanceof IUIElement)
|
||||
el.create();
|
||||
}
|
||||
*/
|
||||
await IUI.created(e);
|
||||
|
||||
await IUI.render(e, value[i], false);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import IUIElement from "../Core/IUIElement.js";
|
||||
import { IUI } from "../Core/IUI.js";
|
||||
import Router from "./Router.js";
|
||||
import RefsCollection from "../Core/RefsCollection.js";
|
||||
|
||||
export default IUI.module(class Route extends IUIElement {
|
||||
|
||||
@ -8,7 +9,7 @@ export default IUI.module(class Route extends IUIElement {
|
||||
super();
|
||||
|
||||
this.routes = [];
|
||||
this.refs = {};
|
||||
this.refs = new RefsCollection(this);
|
||||
|
||||
this._register("show");
|
||||
this._register("hide");
|
||||
@ -21,6 +22,10 @@ export default IUI.module(class Route extends IUIElement {
|
||||
return await super.setData(value);
|
||||
}
|
||||
|
||||
get scope(){
|
||||
return {route: this, view: this};
|
||||
}
|
||||
|
||||
_updateLinks() {
|
||||
for (var i = 0; i < this.children.length; i++) {
|
||||
if (this.children[i] instanceof Route) {
|
||||
@ -111,9 +116,9 @@ export default IUI.module(class Route extends IUIElement {
|
||||
if (window?.app?.loaded)
|
||||
{
|
||||
await IUI.create(this);
|
||||
IUI.bind(this, true, "route:" + src, this.scope);
|
||||
this.refs._build();
|
||||
await IUI.created(this);
|
||||
IUI.bind(this, this, "route:" + src);
|
||||
|
||||
await IUI.render(this, this._data, true);
|
||||
}
|
||||
|
||||
@ -152,7 +157,7 @@ export default IUI.module(class Route extends IUIElement {
|
||||
|
||||
created()
|
||||
{
|
||||
|
||||
this.refs._build();
|
||||
}
|
||||
|
||||
set(value) {
|
||||
|
@ -1,9 +1,11 @@
|
||||
import IUIElement from "../Core/IUIElement.js";
|
||||
import { IUI } from "../Core/IUI.js";
|
||||
import RefsCollection from "../Core/RefsCollection.js";
|
||||
|
||||
export default IUI.module(class CodePreview extends IUIElement {
|
||||
constructor(properties) {
|
||||
constructor() {
|
||||
super();
|
||||
this.refs = new RefsCollection(this);
|
||||
}
|
||||
|
||||
async create() {
|
||||
@ -30,7 +32,7 @@ export default IUI.module(class CodePreview extends IUIElement {
|
||||
let self = this;
|
||||
this.editor.addEventListener("input", function() {
|
||||
self._code = self.editor.innerText.trim();
|
||||
self.update();
|
||||
self.updatePreview();
|
||||
}, false);
|
||||
|
||||
this.preview = document.createElement("div");
|
||||
@ -44,11 +46,18 @@ export default IUI.module(class CodePreview extends IUIElement {
|
||||
this.append(this.content);
|
||||
this.field = this.getAttribute("field");
|
||||
|
||||
await this.update();
|
||||
//await this.updatePreview();
|
||||
}
|
||||
|
||||
|
||||
async update() {
|
||||
async created(){
|
||||
await this.updatePreview();
|
||||
}
|
||||
|
||||
get scope(){
|
||||
return {view: this, refs: this.refs};
|
||||
}
|
||||
|
||||
async updatePreview() {
|
||||
|
||||
if (this._updating)
|
||||
return;
|
||||
@ -56,10 +65,15 @@ export default IUI.module(class CodePreview extends IUIElement {
|
||||
this._updating = true;
|
||||
this.preview.innerHTML = this._code;
|
||||
|
||||
await IUI.create(this.preview);
|
||||
await IUI.created(this.preview);
|
||||
IUI.bind(this.preview, this.preview, "preview");
|
||||
await IUI.render(this.preview, this._data, true);
|
||||
|
||||
if (window.app?.loaded)
|
||||
{
|
||||
await IUI.create(this.preview);
|
||||
await IUI.created(this.preview);
|
||||
IUI.bind(this.preview, true, "preview", this.scope);
|
||||
this.refs._build();
|
||||
await IUI.render(this.preview, this._data, true);
|
||||
}
|
||||
|
||||
this._updating = false;
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ export default IUI.module(class Select extends IUIElement {
|
||||
|
||||
///console.log("Append", this.menu);
|
||||
await this.menu.create();
|
||||
IUI.bind(this.menu, this, "menu");
|
||||
IUI.bind(this.menu, false, "menu");
|
||||
await IUI.create(this.menu);
|
||||
|
||||
//this._make_bindings(this.menu);
|
||||
|
@ -909,7 +909,8 @@ export default IUI.module(class Table extends IUIElement {
|
||||
|
||||
//this._make_bindings(cl)
|
||||
|
||||
IUI.bind(cl, this, "table");
|
||||
IUI.bind(cl, false, "table",
|
||||
IUI.extend(this.__i_bindings, {index: i}, true));
|
||||
|
||||
tr.appendChild(cl);
|
||||
|
||||
|