Simple observed object reactivity
This commit is contained in:
@@ -26,24 +26,6 @@ window.testSuites.push( class testObservedObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WorksWithShadow() {
|
|
||||||
// window.Form = class Form extends ObservedObject {
|
|
||||||
// id
|
|
||||||
// path
|
|
||||||
// $canvasPosition
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class File extends Shadow {
|
|
||||||
// $$form = Form.decode({id: "123", path: "/", canvasPosition: "25|25"})
|
|
||||||
// }
|
|
||||||
|
|
||||||
// window.register(File, "file-1")
|
|
||||||
// let file = window.File()
|
|
||||||
// console.log(file, p())
|
|
||||||
|
|
||||||
// return "Not yet"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ChangingObjChangesInstance() {
|
// ChangingObjChangesInstance() {
|
||||||
// class Form extends ObservedObject {
|
// class Form extends ObservedObject {
|
||||||
// id
|
// id
|
||||||
|
|||||||
39
Test/params.test.js
Normal file
39
Test/params.test.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
window.testSuites.push( class testParams {
|
||||||
|
|
||||||
|
Args() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Default() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2/3
|
||||||
|
|
||||||
|
DefaultArgs() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorArgs() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorDefault() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3/3
|
||||||
|
|
||||||
|
ConstructorDefaultArgs() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorDefault() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
@@ -5,7 +5,6 @@ window.testSuites.push( class testRender {
|
|||||||
$form
|
$form
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
console.log("render", window.rendering)
|
|
||||||
p(this.form.data)
|
p(this.form.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +30,6 @@ window.testSuites.push( class testRender {
|
|||||||
$name
|
$name
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
console.log("render", window.rendering)
|
|
||||||
p(this.name)
|
p(this.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +53,6 @@ window.testSuites.push( class testRender {
|
|||||||
$name = "asd"
|
$name = "asd"
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
console.log("render", window.rendering)
|
|
||||||
p(this.name)
|
p(this.name)
|
||||||
p("asd")
|
p("asd")
|
||||||
}
|
}
|
||||||
@@ -69,14 +66,100 @@ window.testSuites.push( class testRender {
|
|||||||
const file = File()
|
const file = File()
|
||||||
file.name = "hey123"
|
file.name = "hey123"
|
||||||
|
|
||||||
let childs = Array.from(file.children)
|
|
||||||
childs.forEach((el) => {
|
|
||||||
console.log(el.innerText)
|
|
||||||
})
|
|
||||||
|
|
||||||
if(file.children[1].innerText === "hey123") {
|
if(file.children[1].innerText === "hey123") {
|
||||||
return "Paragraph innertext falsely changed"
|
return "Paragraph innertext falsely changed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DefaultObservedObject() {
|
||||||
|
window.Form = class Form extends ObservedObject {
|
||||||
|
id
|
||||||
|
path
|
||||||
|
$canvasPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
class File extends Shadow {
|
||||||
|
$$form = Form.decode({id: "123", path: "/", canvasPosition: "25|25"})
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p(this.form.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.register(File, "file-1")
|
||||||
|
let file = window.File()
|
||||||
|
|
||||||
|
if(file.firstChild?.innerText !== "/") {
|
||||||
|
return "Path is not inside of paragraph tag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObservedObject() {
|
||||||
|
let Form = class Form extends ObservedObject {
|
||||||
|
id
|
||||||
|
$path
|
||||||
|
$children
|
||||||
|
$canvasPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
let object = Form.decode({id: "123", path: "/", children: [], canvasPosition: "25|25"});
|
||||||
|
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$$form
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p(this.form.path)
|
||||||
|
}
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
let file = File(object)
|
||||||
|
|
||||||
|
if(file.firstChild?.innerText !== "/") {
|
||||||
|
return "Path is not inside of paragraph tag"
|
||||||
|
}
|
||||||
|
|
||||||
|
object.path = "/asd"
|
||||||
|
if(file.form.path !== "/asd") {
|
||||||
|
return "Path did not change when changing original object"
|
||||||
|
}
|
||||||
|
if(file.firstChild?.innerText !== "/asd") {
|
||||||
|
return "Observed Object did not cause a reactive change"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObservedObjectWithArray() {
|
||||||
|
// let Form = class Form extends ObservedObject {
|
||||||
|
// id
|
||||||
|
// $path
|
||||||
|
// $children
|
||||||
|
// $canvasPosition
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let object = Form.decode({id: "123", path: "/", children: [], canvasPosition: "25|25"});
|
||||||
|
|
||||||
|
// register(class File extends Shadow {
|
||||||
|
// $$form
|
||||||
|
|
||||||
|
// render = () => {
|
||||||
|
// p(this.form.path)
|
||||||
|
// }
|
||||||
|
// }, randomName("file"))
|
||||||
|
|
||||||
|
// let file = File(object)
|
||||||
|
|
||||||
|
// if(file.firstChild?.innerText !== "/") {
|
||||||
|
// return "Path is not inside of paragraph tag"
|
||||||
|
// }
|
||||||
|
|
||||||
|
// file.form.children.push("hello")
|
||||||
|
|
||||||
|
// object.path = "/asd"
|
||||||
|
// if(file.form.path !== "/asd") {
|
||||||
|
// return "Path did not change when changing original object"
|
||||||
|
// }
|
||||||
|
// if(file.firstChild?.innerText !== "/asd") {
|
||||||
|
// return "Observed Object did not cause a reactive change"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -124,7 +124,31 @@ window.testSuites.push( class testShadow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CannotAddUndefinedProperties() {
|
// this needs to be fixed
|
||||||
|
// CannotAddUndefinedProperties() {
|
||||||
|
// register(class File extends Shadow {
|
||||||
|
|
||||||
|
// render = () => {
|
||||||
|
// p("boi")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// constructor() {
|
||||||
|
// super()
|
||||||
|
// this.hey = "unallowed"
|
||||||
|
// }
|
||||||
|
// }, randomName("file"))
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// const file = File()
|
||||||
|
// return "Did not throw error!"
|
||||||
|
// } catch(e) {
|
||||||
|
// if(!e.message.includes("Extensible")) {
|
||||||
|
// throw e
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
CannotAddUndefinedPropertiesAfterConstructor() {
|
||||||
register(class File extends Shadow {
|
register(class File extends Shadow {
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
@@ -133,21 +157,21 @@ window.testSuites.push( class testShadow {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.hey = "unallowed"
|
|
||||||
}
|
}
|
||||||
}, randomName("file"))
|
}, randomName("file"))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const file = File()
|
const file = File()
|
||||||
|
file.hey = "unallowed"
|
||||||
return "Did not throw error!"
|
return "Did not throw error!"
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if(!e.message.includes("Extensible")) {
|
if(!e.message.includes("extensible")) {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetNonStateFields() {
|
NonStateFieldsGetSet() {
|
||||||
register(class File extends Shadow {
|
register(class File extends Shadow {
|
||||||
nonStateField
|
nonStateField
|
||||||
|
|
||||||
@@ -157,9 +181,29 @@ window.testSuites.push( class testShadow {
|
|||||||
}, randomName("file"))
|
}, randomName("file"))
|
||||||
|
|
||||||
const file = File("asd")
|
const file = File("asd")
|
||||||
if(!file.nonStateField === "asd") {
|
if(!(file.nonStateField === "asd")) {
|
||||||
return "Did not set field!"
|
return "Did not set field!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AllFieldsMustBeSet() {
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$field1
|
||||||
|
$field2
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
try {
|
||||||
|
const file = File("asd")
|
||||||
|
console.log(file.field1, file.field2)
|
||||||
|
return "No error thrown"
|
||||||
|
} catch(e) {
|
||||||
|
if(!e.message.includes("field2\" must be initialized")) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
363
index.js
363
index.js
@@ -145,13 +145,39 @@ function getSafariVersion() {
|
|||||||
|
|
||||||
/* REGISTER */
|
/* REGISTER */
|
||||||
class ObservedObject {
|
class ObservedObject {
|
||||||
|
constructor() {
|
||||||
|
this._observers = {}
|
||||||
|
}
|
||||||
|
|
||||||
static decode(obj) {
|
static decode(obj) {
|
||||||
let instance = new this()
|
let instance = new this()
|
||||||
|
|
||||||
Object.keys(instance).forEach((key) => {
|
Object.keys(instance).forEach((key) => {
|
||||||
if(key[0] === "$") {
|
if(key[0] === "$") {
|
||||||
key = key.substring(1)
|
key = key.slice(1)
|
||||||
|
instance._observers[key] = new Map()
|
||||||
|
const backingFieldName = `_${key}`;
|
||||||
|
Object.defineProperty(instance, key, {
|
||||||
|
set: function(newValue) {
|
||||||
|
instance[backingFieldName] = newValue;
|
||||||
|
for (let [observer, properties] of instance._observers[key]) {
|
||||||
|
for (let property of properties) {
|
||||||
|
observer[property] = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
Registry.lastState.push(key)
|
||||||
|
Registry.lastState.push(instance[backingFieldName])
|
||||||
|
return instance[backingFieldName];
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
delete instance["$" + key]
|
||||||
}
|
}
|
||||||
console.log(key, obj[key])
|
|
||||||
if(obj[key]) {
|
if(obj[key]) {
|
||||||
instance[key] = obj[key]
|
instance[key] = obj[key]
|
||||||
} else {
|
} else {
|
||||||
@@ -160,6 +186,7 @@ class ObservedObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,6 +205,200 @@ window.Shadow = class Shadow extends HTMLElement {
|
|||||||
|
|
||||||
window.Registry = class Registry {
|
window.Registry = class Registry {
|
||||||
|
|
||||||
|
static initReactivity(elem, name, value) {
|
||||||
|
let parent = window.rendering[window.rendering.length-1]
|
||||||
|
|
||||||
|
if(Registry.lastState.length === 3) {
|
||||||
|
let [objName, objField, fieldValue] = Registry.lastState
|
||||||
|
if(!objName) return;
|
||||||
|
|
||||||
|
let valueCheck = parent[objName][objField]
|
||||||
|
if(valueCheck && valueCheck === value) {
|
||||||
|
if(!parent[objName]._observers[objField].get(elem)) {
|
||||||
|
parent[objName]._observers[objField].set(elem, [])
|
||||||
|
}
|
||||||
|
parent[objName]._observers[objField].get(elem).push(name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let [stateUsed, stateValue] = Registry.lastState
|
||||||
|
if(!stateUsed) return;
|
||||||
|
|
||||||
|
if(stateUsed && parent[stateUsed] === value) {
|
||||||
|
if(!parent._observers[stateUsed].get(elem)) {
|
||||||
|
parent._observers[stateUsed].set(elem, [])
|
||||||
|
}
|
||||||
|
parent._observers[stateUsed].get(elem).push(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Registry.lastState = []
|
||||||
|
}
|
||||||
|
|
||||||
|
static render = (el, parent) => {
|
||||||
|
let renderParent = window.rendering[window.rendering.length-1]
|
||||||
|
if(renderParent) {
|
||||||
|
renderParent.appendChild(el)
|
||||||
|
}
|
||||||
|
window.rendering.push(el)
|
||||||
|
el.render()
|
||||||
|
window.rendering.pop(el)
|
||||||
|
}
|
||||||
|
|
||||||
|
static testInitialized(el) {
|
||||||
|
let fields = Object.keys(el).filter(key =>
|
||||||
|
typeof el[key] !== 'function' && key !== "_observers"
|
||||||
|
)
|
||||||
|
|
||||||
|
for(let field of fields) {
|
||||||
|
if(el[field] === undefined) {
|
||||||
|
throw new Error(`Quill: field "${field}" must be initialized`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static construct = (elem) => { // After default params are set, but before body of constructor
|
||||||
|
const params = window.Registry.currentParams
|
||||||
|
const allNames = Object.keys(elem).filter(key => typeof elem[key] !== 'function')
|
||||||
|
const stateNames = allNames.filter(field => /^[$][^$]/.test(field)).map(str => str.substring(1));
|
||||||
|
const observedObjectNames = allNames.filter(field => /^[$][$][^$]/.test(field)).map(str => str.substring(2));
|
||||||
|
|
||||||
|
/*
|
||||||
|
State Reactivity [stateName].get(elem).push(attribute)
|
||||||
|
_observers = {
|
||||||
|
name: Map(
|
||||||
|
<p>: [innerText, background]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
OO Reactivity: [objectName][objectField].get(elem).push(attribute)
|
||||||
|
$$form: extends ObservedObject {
|
||||||
|
_observers = {
|
||||||
|
canvasPosition: Map(
|
||||||
|
<p>: [position]
|
||||||
|
),
|
||||||
|
path: Map(
|
||||||
|
<p>: [innerText]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function makeState(elem, stateNames, params) {
|
||||||
|
elem._observers = {}
|
||||||
|
|
||||||
|
// State -> Attributes: set each state value as getter and setter
|
||||||
|
stateNames.forEach(name => {
|
||||||
|
const backingFieldName = `_${name}`;
|
||||||
|
elem._observers[name] = new Map()
|
||||||
|
|
||||||
|
Object.defineProperty(elem, name, {
|
||||||
|
set: function(newValue) {
|
||||||
|
// console.log(`Setting state ${name} to `, newValue);
|
||||||
|
elem[backingFieldName] = newValue; // Use the backing field to store the value
|
||||||
|
elem.setAttribute(name, typeof newValue === "object" ? "{..}" : newValue);
|
||||||
|
for (let [observer, properties] of elem._observers[name]) {
|
||||||
|
for (let property of properties) {
|
||||||
|
observer[property] = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
Registry.lastState = [name, elem[backingFieldName]]
|
||||||
|
// check which elements are observing the
|
||||||
|
return elem[backingFieldName]; // Provide a getter to access the backing field value
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if(elem["$" + name] !== undefined) {
|
||||||
|
elem[name] = elem["$" + name]
|
||||||
|
}
|
||||||
|
|
||||||
|
delete elem["$" + name]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeObservedObjects(elem, objectNames, params) {
|
||||||
|
objectNames.forEach(name => {
|
||||||
|
const backingFieldName = `_${name}`;
|
||||||
|
|
||||||
|
Object.defineProperty(elem, name, {
|
||||||
|
set: function(newValue) {
|
||||||
|
elem[backingFieldName] = newValue;
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
Registry.lastState = [name]
|
||||||
|
return elem[backingFieldName];
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if(elem["$$" + name] !== undefined) {
|
||||||
|
elem[name] = elem["$$" + name]
|
||||||
|
}
|
||||||
|
|
||||||
|
delete elem["$$" + name]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
makeState(elem, stateNames, params)
|
||||||
|
makeObservedObjects(elem, observedObjectNames, params)
|
||||||
|
|
||||||
|
let allNamesCleaned = Object.keys(elem)
|
||||||
|
.filter(key => typeof elem[key] !== 'function' && key !== "_observers" && key !== "_observedObjects")
|
||||||
|
.map(key => key.replace(/^(\$\$|\$)/, ''));
|
||||||
|
|
||||||
|
let i = -1
|
||||||
|
for (let param of params) {
|
||||||
|
i++
|
||||||
|
|
||||||
|
if(i > allNamesCleaned.length) {
|
||||||
|
console.error(`${el.prototype.constructor.name}: too many parameters for field!`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(elem[allNamesCleaned[i]] === undefined) {
|
||||||
|
elem[allNamesCleaned[i]] = param
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static register = (el, tagname) => {
|
||||||
|
let stateVariables = this.parseClassFields(el).filter(field => /^[$][^$]/.test(field)).map(str => str.substring(1));
|
||||||
|
el = this.parseConstructor(el)
|
||||||
|
|
||||||
|
// Observe attributes
|
||||||
|
Object.defineProperty(el, 'observedAttributes', {
|
||||||
|
get: function() {
|
||||||
|
return stateVariables;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Attributes -> State
|
||||||
|
Object.defineProperty(el.prototype, 'attributeChangedCallback', {
|
||||||
|
value: function(name, oldValue, newValue) {
|
||||||
|
const fieldName = `${name}`;
|
||||||
|
let blacklistedValues = ["[object Object]", "{..}", this[fieldName]]
|
||||||
|
if (stateVariables.includes(fieldName) && !blacklistedValues.includes(newValue)) {
|
||||||
|
this[fieldName] = newValue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
customElements.define(tagname, el)
|
||||||
|
|
||||||
|
// Actual Constructor
|
||||||
|
window[el.prototype.constructor.name] = function (...params) {
|
||||||
|
window.Registry.currentParams = params
|
||||||
|
let elIncarnate = new el(...params)
|
||||||
|
Registry.render(elIncarnate)
|
||||||
|
return elIncarnate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static parseClassFields(classObject) {
|
static parseClassFields(classObject) {
|
||||||
let str = classObject.toString();
|
let str = classObject.toString();
|
||||||
const lines = str.split('\n');
|
const lines = str.split('\n');
|
||||||
@@ -243,7 +464,6 @@ window.Registry = class Registry {
|
|||||||
|
|
||||||
if(superCallFound) {
|
if(superCallFound) {
|
||||||
let modifiedStr = modifiedLines.join('\n');
|
let modifiedStr = modifiedLines.join('\n');
|
||||||
console.log(modifiedStr)
|
|
||||||
return eval('(' + modifiedStr + ')');
|
return eval('(' + modifiedStr + ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,134 +481,6 @@ window.Registry = class Registry {
|
|||||||
return eval('(' + str + ')');
|
return eval('(' + str + ')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static testInitialized() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static render = (el, parent) => {
|
|
||||||
let renderParent = window.rendering[window.rendering.length-1]
|
|
||||||
if(renderParent) {
|
|
||||||
renderParent.appendChild(el)
|
|
||||||
}
|
|
||||||
window.rendering.push(el)
|
|
||||||
el.render()
|
|
||||||
window.rendering.pop(el)
|
|
||||||
}
|
|
||||||
|
|
||||||
static construct = (elem) => {
|
|
||||||
const params = window.Registry.currentParams
|
|
||||||
const allNames = Object.keys(elem).filter(key => typeof elem[key] !== 'function')
|
|
||||||
const fieldNames = allNames.filter(field => /^[^\$]/.test(field))
|
|
||||||
const stateNames = allNames.filter(field => /^[$][^$]/.test(field)).map(str => str.substring(1));
|
|
||||||
const observedObjectNames = allNames.filter(field => /^[$][$][^$]/.test(field)).map(str => str.substring(2));
|
|
||||||
|
|
||||||
function makeState(elem, stateNames, params) {
|
|
||||||
elem._observers = {}
|
|
||||||
|
|
||||||
// State -> Attributes: set each state value as getter and setter
|
|
||||||
stateNames.forEach(name => {
|
|
||||||
const backingFieldName = `_${name}`;
|
|
||||||
elem._observers[name] = new Map()
|
|
||||||
|
|
||||||
Object.defineProperty(elem, name, {
|
|
||||||
set: function(newValue) {
|
|
||||||
console.log(`Setting state ${name} to `, newValue);
|
|
||||||
elem[backingFieldName] = newValue; // Use the backing field to store the value
|
|
||||||
elem.setAttribute(name, typeof newValue === "object" ? "{..}" : newValue);
|
|
||||||
console.log(elem._observers)
|
|
||||||
for (let [observer, properties] of elem._observers[name]) {
|
|
||||||
for (let property of properties) {
|
|
||||||
observer[property] = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
get: function() {
|
|
||||||
Registry.lastState = [name, elem[backingFieldName]]
|
|
||||||
// check which elements are observing the
|
|
||||||
return elem[backingFieldName]; // Provide a getter to access the backing field value
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if(elem["$" + name] !== undefined) { // User provided a default value
|
|
||||||
elem[name] = elem["$" + name]
|
|
||||||
delete elem["$" + name]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Match params to state names
|
|
||||||
switch(stateNames.length) {
|
|
||||||
case 0:
|
|
||||||
console.log("No state variables passed in, returning")
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
let i = -1
|
|
||||||
for (let param of params) {
|
|
||||||
i++
|
|
||||||
|
|
||||||
if(i > stateNames.length) {
|
|
||||||
console.error(`${el.prototype.constructor.name}: too many parameters for state!`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if(elem[stateNames[i]] === undefined) {
|
|
||||||
elem[stateNames[i]] = param
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if all state variables are set. If not set, check if it is a user-initted value
|
|
||||||
for(let state of stateNames) {
|
|
||||||
if(elem[state] === undefined) {
|
|
||||||
console.error(`Quill: state "${state}" must be initialized`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeObservedObjects(elem, observedObjectNames, params) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
makeState(elem, stateNames, params)
|
|
||||||
makeObservedObjects(elem, observedObjectNames, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
static register = (el, tagname) => {
|
|
||||||
let stateVariables = this.parseClassFields(el).filter(field => /^[$][^$]/.test(field)).map(str => str.substring(1));
|
|
||||||
el = this.parseConstructor(el)
|
|
||||||
|
|
||||||
// Observe attributes
|
|
||||||
Object.defineProperty(el, 'observedAttributes', {
|
|
||||||
get: function() {
|
|
||||||
return stateVariables;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Attributes -> State
|
|
||||||
Object.defineProperty(el.prototype, 'attributeChangedCallback', {
|
|
||||||
value: function(name, oldValue, newValue) {
|
|
||||||
const fieldName = `${name}`;
|
|
||||||
let blacklistedValues = ["[object Object]", "{..}", this[fieldName]]
|
|
||||||
if (stateVariables.includes(fieldName) && !blacklistedValues.includes(newValue)) {
|
|
||||||
this[fieldName] = newValue;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
|
|
||||||
customElements.define(tagname, el)
|
|
||||||
|
|
||||||
// Actual Constructor
|
|
||||||
window[el.prototype.constructor.name] = function (...params) {
|
|
||||||
window.Registry.currentParams = params
|
|
||||||
let elIncarnate = new el(...params)
|
|
||||||
Registry.render(elIncarnate)
|
|
||||||
return elIncarnate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DEFAULT WRAPPERS */
|
/* DEFAULT WRAPPERS */
|
||||||
@@ -411,16 +503,9 @@ window.img = function img({width="", height="", src=""}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.p = function p(innerText) {
|
window.p = function p(innerText) {
|
||||||
let parent = window.rendering[window.rendering.length-1]
|
|
||||||
let para = document.createElement("p")
|
let para = document.createElement("p")
|
||||||
para.innerText = innerText
|
para.innerText = innerText
|
||||||
if(Registry.lastState[0] && parent[Registry.lastState[0]] === innerText) {
|
Registry.initReactivity(para, "innerText", innerText)
|
||||||
if(!parent._observers[Registry.lastState[0]][para]) {
|
|
||||||
parent._observers[Registry.lastState[0]].set(para, [])
|
|
||||||
}
|
|
||||||
parent._observers[Registry.lastState[0]].get(para).push("innerText")
|
|
||||||
}
|
|
||||||
Registry.lastState = []
|
|
||||||
Registry.render(para)
|
Registry.render(para)
|
||||||
return para
|
return para
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user