generate styles dynamically with universal reactivity, test for conflicting state names
This commit is contained in:
@@ -277,4 +277,20 @@ window.testSuites.push( class testInit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConflictingPropertyNameThrowsError() {
|
||||
register(class SidebarFile extends Shadow {
|
||||
$width = 0
|
||||
|
||||
}, randomName("sb-file"))
|
||||
|
||||
try {
|
||||
SidebarFile()
|
||||
return "Did not throw error!"
|
||||
} catch(e) {
|
||||
if(!e.message.includes(`Property name "width" is not valid`)) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
183
index.js
183
index.js
@@ -122,39 +122,14 @@ window.quillStyles.update = function(tag, string) {
|
||||
|
||||
/* STRING TRANSLATORS */
|
||||
|
||||
window.css = function css(cssString) {
|
||||
// let container = document.querySelector("style#quillStyles");
|
||||
// if(!container) {
|
||||
// container = document.createElement('style');
|
||||
// container.id = "quillStyles";
|
||||
// document.head.appendChild(container);
|
||||
// }
|
||||
|
||||
// let primarySelector = cssString.substring(0, cssString.indexOf("{")).trim();
|
||||
// primarySelector = primarySelector.replace(/\*/g, "all");
|
||||
// primarySelector = primarySelector.replace(/#/g, "id-");
|
||||
// primarySelector = primarySelector.replace(/,/g, "");
|
||||
// let stylesheet = container.querySelector(`:scope > style[id='${primarySelector}']`)
|
||||
// if(!stylesheet) {
|
||||
// stylesheet = document.createElement('style');
|
||||
// stylesheet.id = primarySelector;
|
||||
// stylesheet.appendChild(document.createTextNode(cssString));
|
||||
// container.appendChild(stylesheet);
|
||||
// } else {
|
||||
// stylesheet.innerText = cssString
|
||||
// }
|
||||
}
|
||||
|
||||
window.html = function html(htmlString) {
|
||||
let container = document.createElement('div');
|
||||
container.innerHTML = htmlString;
|
||||
|
||||
// If there's only one child, return it directly
|
||||
if (container.children.length === 1) {
|
||||
return container.children[0];
|
||||
}
|
||||
|
||||
// If there are multiple children, use a DocumentFragment
|
||||
let fragment = document.createDocumentFragment();
|
||||
while (container.firstChild) {
|
||||
fragment.appendChild(container.firstChild);
|
||||
@@ -303,6 +278,27 @@ window.Shadow = class Shadow extends HTMLElement {
|
||||
|
||||
window.Registry = class Registry {
|
||||
|
||||
/*
|
||||
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]
|
||||
)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static initReactivity(elem, name, value) {
|
||||
let parent = window.rendering.last()
|
||||
|
||||
@@ -373,27 +369,6 @@ window.Registry = class Registry {
|
||||
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 = {}
|
||||
|
||||
@@ -404,18 +379,21 @@ window.Registry = class Registry {
|
||||
|
||||
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) {
|
||||
if(Array.isArray(property)) {
|
||||
observer[property[0]][property[1]] = newValue;
|
||||
} else {
|
||||
observer[property] = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
get: function() {
|
||||
Registry.lastState = [name, elem[backingFieldName]]
|
||||
return elem[backingFieldName]; // Provide a getter to access the backing field value
|
||||
return elem[backingFieldName];
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
@@ -456,6 +434,12 @@ window.Registry = class Registry {
|
||||
makeState(elem, stateNames, params)
|
||||
makeObservedObjects(elem, observedObjectNames, params)
|
||||
|
||||
for(let name of allNames) {
|
||||
if(name.replace(/^\$+/, '') in elem.style) {
|
||||
throw new Error(`Quill: Property name "${name.replace(/^\$+/, '')}" is not valid`)
|
||||
}
|
||||
}
|
||||
|
||||
let i = -1
|
||||
for (let param of params) {
|
||||
i++
|
||||
@@ -662,6 +646,21 @@ window.VStack = function (cb = () => {}) {
|
||||
}
|
||||
}
|
||||
|
||||
/* SHAPES */
|
||||
|
||||
window.Circle = function(text = "") {
|
||||
let div = document.createElement("div")
|
||||
div.innerText = text
|
||||
div.style.borderRadius = "100%"
|
||||
div.style.width = "20px"
|
||||
div.style.height = "20px"
|
||||
div.style.background = "black"
|
||||
div.style.color = "white"
|
||||
div.style.textAlign = "center"
|
||||
Registry.render(div)
|
||||
return div
|
||||
}
|
||||
|
||||
/* PROTOTYPE FUNCTIONS */
|
||||
|
||||
Array.prototype.last = function() {
|
||||
@@ -709,63 +708,52 @@ HTMLElement.prototype.class = function(classNames) {
|
||||
|
||||
/* PROTOTYPE STYLING */
|
||||
|
||||
HTMLElement.prototype.styleVar = function(name, value) {
|
||||
this.style.setProperty(name, value)
|
||||
return this
|
||||
}
|
||||
function extendHTMLElementWithStyleSetters() {
|
||||
const styleProps = Object.keys(window.getComputedStyle(document.head));
|
||||
const filteredProps = styleProps.filter(prop => /[a-zA-Z]/.test(prop));
|
||||
|
||||
HTMLElement.prototype.color = function(value) {
|
||||
this.style.color = value
|
||||
return this
|
||||
filteredProps.forEach(prop => {
|
||||
if(prop === "translate") return
|
||||
HTMLElement.prototype[prop] = function(value) {
|
||||
this.style[prop] = value;
|
||||
Registry.initReactivity(this, ["style", prop], value);
|
||||
return this;
|
||||
};
|
||||
});
|
||||
}
|
||||
extendHTMLElementWithStyleSetters();
|
||||
|
||||
HTMLElement.prototype.background = function(value) {
|
||||
this.style.backgroundColor = value
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.fontSize = function(value) {
|
||||
this.style.fontSize = value
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.borderRadius = function(value) {
|
||||
this.style.borderRadius = value
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.padding = function(direction, amount) {
|
||||
HTMLElement.prototype.padding = function(direction, value) {
|
||||
const directionName = `padding${direction.charAt(0).toUpperCase()}${direction.slice(1)}`;
|
||||
if (typeof amount === 'number') {
|
||||
this.style[directionName] = `${amount}px`;
|
||||
if (typeof value === 'number') {
|
||||
this.style[directionName] = `${value}px`;
|
||||
} else {
|
||||
this.style[directionName] = amount;
|
||||
this.style[directionName] = value;
|
||||
}
|
||||
Registry.initReactivity(this, ["style", directionName], value);
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.outline = function(value) {
|
||||
this.style.outline = value
|
||||
HTMLElement.prototype.width = function(value, unit = "px") {
|
||||
this.style.width = value + unit
|
||||
Registry.initReactivity(this, ["style", "width"], value);
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.maxWidth = function(value) {
|
||||
this.style.maxWidth = value
|
||||
HTMLElement.prototype.height = function(value, unit = "px") {
|
||||
this.style.height = value + unit
|
||||
Registry.initReactivity(this, ["style", "height"], value);
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.margin = function(direction, amount) {
|
||||
HTMLElement.prototype.margin = function(direction, value) {
|
||||
const directionName = `margin${direction.charAt(0).toUpperCase()}${direction.slice(1)}`;
|
||||
if (typeof amount === 'number') {
|
||||
this.style[directionName] = `${amount}px`;
|
||||
if (typeof value === 'number') {
|
||||
this.style[directionName] = `${value}px`;
|
||||
} else {
|
||||
this.style[directionName] = amount;
|
||||
this.style[directionName] = value;
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.transform = function(value) {
|
||||
this.style.transform = value
|
||||
Registry.initReactivity(this, ["style", directionName], value);
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -775,11 +763,12 @@ HTMLElement.prototype.positionType = function (value) {
|
||||
return;
|
||||
}
|
||||
this.style.position = value
|
||||
Registry.initReactivity(this, ["style", "position"], value);
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.position = function({x, y} = {}) {
|
||||
if(!x || !y) {
|
||||
if(!(typeof x === 'number') || !(typeof x === 'number')) {
|
||||
console.error("HTMLElement.position: must have valid x and y values: {x: 12, y: 23} where x and y are percentages")
|
||||
return;
|
||||
}
|
||||
@@ -789,19 +778,11 @@ HTMLElement.prototype.position = function({x, y} = {}) {
|
||||
}
|
||||
this.style.left = `${x}%`
|
||||
this.style.top = `${y}%`
|
||||
Registry.initReactivity(this, ["style", "top"], y);
|
||||
Registry.initReactivity(this, ["style", "left"], x);
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.overflow = function(value) {
|
||||
if(!(value === "visible" || value === "hidden" || value === "clip" || value === "scroll" || value === "auto")) {
|
||||
console.error("HTMLElement.overlflow: must have valid overflow value!")
|
||||
return;
|
||||
}
|
||||
this.style.overflow = value;
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/* PROTOTYPE EVENTS */
|
||||
|
||||
HTMLElement.prototype.onClick = function(func) {
|
||||
@@ -809,5 +790,11 @@ HTMLElement.prototype.onClick = function(func) {
|
||||
return this
|
||||
}
|
||||
|
||||
HTMLElement.prototype.onHover = function(cb) {
|
||||
this.addEventListener("mouseover", () => cb(true))
|
||||
this.addEventListener("mouseleave", () => cb(false))
|
||||
return this
|
||||
}
|
||||
|
||||
window.register = Registry.register
|
||||
window.rendering = []
|
||||
Reference in New Issue
Block a user