12.16, 12.17 changes
This commit is contained in:
239
index.js
239
index.js
@@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
Sam Russell
|
Sam Russell
|
||||||
Captured Sun
|
Captured Sun
|
||||||
|
12.17.25 - [Hyperia] - adding width, height functions. adding "e" to onClick. adding the non-window $$ funcs.
|
||||||
|
12.16.25 - [comalyr] - State
|
||||||
11.25.25.1 - Added minHeight and minWidth to be counted as numerical styles
|
11.25.25.1 - Added minHeight and minWidth to be counted as numerical styles
|
||||||
11.25.25 - Added onChange for check boxes, added setQuery / onQueryChanged for easy filtering
|
11.25.25 - Added onChange for check boxes, added setQuery / onQueryChanged for easy filtering
|
||||||
11.24.25 - Fixing onClick because it was reversed, adding event to onHover params
|
11.24.25 - Fixing onClick because it was reversed, adding event to onHover params
|
||||||
@@ -68,6 +70,12 @@ window.$ = function(selector, el = document) {
|
|||||||
window.$$ = function(selector, el = document) {
|
window.$$ = function(selector, el = document) {
|
||||||
return Array.from(el.querySelectorAll(selector))
|
return Array.from(el.querySelectorAll(selector))
|
||||||
}
|
}
|
||||||
|
HTMLElement.prototype.$$ = function(selector) {
|
||||||
|
return window.$$(selector, this)
|
||||||
|
}
|
||||||
|
DocumentFragment.prototype.$$ = function(selector) {
|
||||||
|
return window.$$(selector, this)
|
||||||
|
}
|
||||||
|
|
||||||
/* CONSOLE */
|
/* CONSOLE */
|
||||||
|
|
||||||
@@ -163,6 +171,7 @@ Object.defineProperty(Array.prototype, 'last', {
|
|||||||
|
|
||||||
window.quill = {
|
window.quill = {
|
||||||
rendering: [],
|
rendering: [],
|
||||||
|
lastState: null,
|
||||||
|
|
||||||
render: (el) => {
|
render: (el) => {
|
||||||
if(el instanceof Shadow) {
|
if(el instanceof Shadow) {
|
||||||
@@ -193,19 +202,6 @@ window.quill = {
|
|||||||
quill.rendering.pop()
|
quill.rendering.pop()
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPage: () => {
|
|
||||||
let URL = window.location.pathname
|
|
||||||
if(!window.routes[URL]) {
|
|
||||||
throw new Error("No URL for this route: ", URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
let pageClass = window[routes[URL]]
|
|
||||||
document.title = pageClass.title ?? document.title
|
|
||||||
document.body.innerHTML = ""
|
|
||||||
let page = new pageClass()
|
|
||||||
quill.render(page)
|
|
||||||
},
|
|
||||||
|
|
||||||
isStack: (el) => {
|
isStack: (el) => {
|
||||||
return el.classList.contains("HStack") || el.classList.contains("ZStack") || el.classList.contains("VStack")
|
return el.classList.contains("HStack") || el.classList.contains("ZStack") || el.classList.contains("VStack")
|
||||||
},
|
},
|
||||||
@@ -216,6 +212,7 @@ window.Shadow = class extends HTMLElement {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.register = (el, tagname) => {
|
window.register = (el, tagname) => {
|
||||||
if (typeof el.prototype.render !== 'function') {
|
if (typeof el.prototype.render !== 'function') {
|
||||||
throw new Error("Element must have a render: " + el.prototype.constructor.name)
|
throw new Error("Element must have a render: " + el.prototype.constructor.name)
|
||||||
@@ -230,6 +227,42 @@ window.register = (el, tagname) => {
|
|||||||
|
|
||||||
window[el.prototype.constructor.name] = function (...params) {
|
window[el.prototype.constructor.name] = function (...params) {
|
||||||
let instance = new el(...params)
|
let instance = new el(...params)
|
||||||
|
if(instance.state) {
|
||||||
|
let proxy = new Proxy(instance.state, {
|
||||||
|
get(target, prop, receiver) {
|
||||||
|
if (typeof prop === "symbol") { // Ignore internal / symbol accesses
|
||||||
|
return Reflect.get(target, prop, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
quill.lastLastState = quill.lastState
|
||||||
|
quill.lastState = prop;
|
||||||
|
return Reflect.get(target, prop, receiver);
|
||||||
|
},
|
||||||
|
set(target, prop, value, receiver) {
|
||||||
|
const oldValue = target[prop];
|
||||||
|
if (oldValue === value) return true;
|
||||||
|
|
||||||
|
const result = Reflect.set(target, prop, value, receiver);
|
||||||
|
instance.stateWatchers[prop].forEach((cb) => cb())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(instance, "state", {
|
||||||
|
value: proxy,
|
||||||
|
writable: false,
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let stateWatchers = {}
|
||||||
|
Object.keys(instance.state).forEach((key) => stateWatchers[key] = [])
|
||||||
|
Object.defineProperty(instance, "stateWatchers", {
|
||||||
|
value: stateWatchers,
|
||||||
|
writable: false,
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
quill.render(instance)
|
quill.render(instance)
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
@@ -314,6 +347,7 @@ function extendHTMLElementWithStyleSetters() {
|
|||||||
case "marginRight":
|
case "marginRight":
|
||||||
|
|
||||||
case "textUnderlineOffset":
|
case "textUnderlineOffset":
|
||||||
|
case "letterSpacing":
|
||||||
|
|
||||||
return "unit-number"
|
return "unit-number"
|
||||||
|
|
||||||
@@ -333,24 +367,27 @@ function extendHTMLElementWithStyleSetters() {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "unit-number":
|
case "unit-number":
|
||||||
HTMLElement.prototype[prop] = function(value, unit = "px") {
|
HTMLElement.prototype[prop] = StyleFunction(function(value, unit = "px") {
|
||||||
if ((typeof value !== "number" || isNaN(value)) && value !== "auto") {
|
|
||||||
throw new Error(`Invalid value for ${prop}: ${value}. Expected a number.`);
|
|
||||||
}
|
|
||||||
if(value === "auto") {
|
if(value === "auto") {
|
||||||
this.style[prop] = value
|
this.style[prop] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
this.style[prop] = value + unit;
|
this.style[prop] = value + unit;
|
||||||
|
if (value !== "" && this.style[prop] === "") {
|
||||||
|
throw new Error(`Invalid CSS value for ${prop}: ` + value + unit);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "string":
|
case "string":
|
||||||
HTMLElement.prototype[prop] = function(value) {
|
HTMLElement.prototype[prop] = StyleFunction(function(value) {
|
||||||
this.style[prop] = value;
|
this.style[prop] = value;
|
||||||
|
if (value !== "" && this.style[prop] === "") {
|
||||||
|
throw new Error(`Invalid CSS value for ${prop}: ` + value);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -358,46 +395,86 @@ function extendHTMLElementWithStyleSetters() {
|
|||||||
|
|
||||||
extendHTMLElementWithStyleSetters();
|
extendHTMLElementWithStyleSetters();
|
||||||
|
|
||||||
|
HTMLElement.prototype.addStateWatcher = function(field, cb) {
|
||||||
|
let parent = this
|
||||||
|
while(!(parent instanceof Shadow)) {
|
||||||
|
parent = parent.parentNode
|
||||||
|
}
|
||||||
|
parent.stateWatchers[field].push(cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently only works for one state variable in the function
|
||||||
|
// Could probably be fixed by just making lastState an array and clearing it out every function call?
|
||||||
|
HTMLElement.prototype.setUpState = function(styleFunc, cb) {
|
||||||
|
let format = (value) => {return Array.isArray(value) ? value : [value]}
|
||||||
|
|
||||||
|
// 1. Run the callback to get the style argument and also update lastState
|
||||||
|
let styleArgs = format(cb())
|
||||||
|
|
||||||
|
// 2. Check if lastState has really been updated. If not, the user-provided cb did not access valid state
|
||||||
|
if(!quill.lastState) {
|
||||||
|
throw new Error("Quill: style state function does not access valid state")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Construct function to run when state changes
|
||||||
|
let onStateChange = () => {
|
||||||
|
styleFunc.call(this, ...format(cb()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Now listen for the state to change
|
||||||
|
this.addStateWatcher(quill.lastState, onStateChange)
|
||||||
|
|
||||||
|
// 5. Run the original function again, this time with the actual arguments
|
||||||
|
quill.lastState = null
|
||||||
|
styleFunc.call(this, ...styleArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StyleFunction(func) {
|
||||||
|
let styleFunction = function(value, unit = "px") {
|
||||||
|
if(typeof value === 'function') {
|
||||||
|
this.setUpState(styleFunction, value)
|
||||||
|
return this
|
||||||
|
} else {
|
||||||
|
func.call(this, value, unit); // ".call" ensures that "this" is correct
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return styleFunction
|
||||||
|
}
|
||||||
|
|
||||||
HTMLElement.prototype.styles = function(cb) {
|
HTMLElement.prototype.styles = function(cb) {
|
||||||
cb.call(this, this)
|
cb.call(this, this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
HTMLElement.prototype.paddingVertical = function(value, unit = "px") {
|
/* Type 1 */
|
||||||
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
|
|
||||||
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
HTMLElement.prototype.paddingVertical = StyleFunction(function(value, unit = "px") {
|
||||||
this.style.paddingTop = value + unit
|
this.style.paddingTop = value + unit
|
||||||
this.style.paddingBottom = value + unit
|
this.style.paddingBottom = value + unit
|
||||||
return this
|
return this
|
||||||
}
|
})
|
||||||
|
|
||||||
HTMLElement.prototype.paddingHorizontal = function(value, unit = "px") {
|
HTMLElement.prototype.paddingHorizontal = StyleFunction(function(value, unit = "px") {
|
||||||
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
|
|
||||||
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
|
||||||
this.style.paddingRight = value + unit
|
this.style.paddingRight = value + unit
|
||||||
this.style.paddingLeft = value + unit
|
this.style.paddingLeft = value + unit
|
||||||
return this
|
return this
|
||||||
}
|
})
|
||||||
|
|
||||||
HTMLElement.prototype.marginVertical = function(value, unit = "px") {
|
HTMLElement.prototype.marginVertical = StyleFunction(function(value, unit = "px") {
|
||||||
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
|
|
||||||
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
|
||||||
this.style.marginTop = value + unit
|
this.style.marginTop = value + unit
|
||||||
this.style.marginBottom = value + unit
|
this.style.marginBottom = value + unit
|
||||||
return this
|
return this
|
||||||
}
|
})
|
||||||
|
|
||||||
HTMLElement.prototype.marginHorizontal = function(value, unit = "px") {
|
HTMLElement.prototype.marginHorizontal = StyleFunction(function(value, unit = "px") {
|
||||||
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
|
|
||||||
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
|
||||||
this.style.marginRight = value + unit
|
this.style.marginRight = value + unit
|
||||||
this.style.marginLeft = value + unit
|
this.style.marginLeft = value + unit
|
||||||
return this
|
return this
|
||||||
}
|
})
|
||||||
|
|
||||||
HTMLElement.prototype.fontSize = function(value, unit = "px") {
|
HTMLElement.prototype.fontSize = StyleFunction(function(value, unit = "px") {
|
||||||
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
|
|
||||||
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
|
||||||
|
|
||||||
switch(value) {
|
switch(value) {
|
||||||
case "6xl":
|
case "6xl":
|
||||||
@@ -441,7 +518,7 @@ HTMLElement.prototype.fontSize = function(value, unit = "px") {
|
|||||||
}
|
}
|
||||||
this.style.fontSize = value + unit
|
this.style.fontSize = value + unit
|
||||||
return this
|
return this
|
||||||
}
|
})
|
||||||
|
|
||||||
function checkPositionType(el) {
|
function checkPositionType(el) {
|
||||||
let computed = window.getComputedStyle(el).position
|
let computed = window.getComputedStyle(el).position
|
||||||
@@ -450,6 +527,26 @@ function checkPositionType(el) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HTMLElement.prototype.width = function(value, unit = "px") {
|
||||||
|
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
|
||||||
|
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
||||||
|
this.style.width = value + unit
|
||||||
|
if(window.getComputedStyle(this).display === "inline") {
|
||||||
|
this.style.display = "block"
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLElement.prototype.height = function(value, unit = "px") {
|
||||||
|
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
|
||||||
|
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
||||||
|
this.style.height = value + unit
|
||||||
|
if(window.getComputedStyle(this).display === "inline") {
|
||||||
|
this.style.display = "block"
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
HTMLElement.prototype.x = function(value, unit = "px") {
|
HTMLElement.prototype.x = function(value, unit = "px") {
|
||||||
if (typeof value !== 'number' || isNaN(value))
|
if (typeof value !== 'number' || isNaN(value))
|
||||||
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
throw new Error(`Invalid value: ${value}. Expected a number.`);
|
||||||
@@ -661,7 +758,7 @@ window.form = function(cb) {
|
|||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
window.input = function(placeholder, width, height) {
|
window.input = function(placeholder = "", width, height) {
|
||||||
let el = document.createElement("input")
|
let el = document.createElement("input")
|
||||||
el.placeholder = placeholder
|
el.placeholder = placeholder
|
||||||
el.style.width = width
|
el.style.width = width
|
||||||
@@ -670,14 +767,18 @@ window.input = function(placeholder, width, height) {
|
|||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
window.label = function(text) {
|
window.label = function(inside) {
|
||||||
let el = document.createElement("label")
|
let el = document.createElement("label")
|
||||||
el.innerText = text
|
if(typeof inside === "string") {
|
||||||
|
el.innerText = inside
|
||||||
|
} else {
|
||||||
|
el.render = inside
|
||||||
|
}
|
||||||
quill.render(el)
|
quill.render(el)
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
window.textarea = function(placeholder) {
|
window.textarea = function(placeholder = "") {
|
||||||
let el = document.createElement("textarea")
|
let el = document.createElement("textarea")
|
||||||
el.placeholder = placeholder
|
el.placeholder = placeholder
|
||||||
quill.render(el)
|
quill.render(el)
|
||||||
@@ -845,8 +946,8 @@ HTMLElement.prototype.onAppear = function(func) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HTMLElement.prototype.onClick = function(func) {
|
HTMLElement.prototype.onClick = function(func) {
|
||||||
const onMouseDown = () => func.call(this, false);
|
const onMouseDown = (e) => func.call(this, false, e);
|
||||||
const onMouseUp = () => func.call(this, true);
|
const onMouseUp = (e) => func.call(this, true, e);
|
||||||
this._storeListener("mousedown", onMouseDown);
|
this._storeListener("mousedown", onMouseDown);
|
||||||
this._storeListener("mouseup", onMouseUp);
|
this._storeListener("mouseup", onMouseUp);
|
||||||
return this;
|
return this;
|
||||||
@@ -929,34 +1030,38 @@ HTMLElement.prototype.onTap = function(cb) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* WHY THIS LISTENER IS THE WAY IT IS:
|
/* WHY THIS LISTENER IS THE WAY IT IS:
|
||||||
- If we dispatch the "navigate" event on the window (as one would expect for a "navigate" event), a listener placed on the element will not pick it up.
|
- We can't just put a listener on the element, because a window "navigate" event won't trigger it
|
||||||
- However, if we add the listener on the window, it won't have the "this" scope that a callback normally would. Which makes it much less useful.
|
- We can't just put a listener on the window, because the "this" variable will only refer to the window
|
||||||
- Then, if we try to add that scope using bind(), it makes the function.toString() unreadable, which means we cannot detect duplicate listeners.
|
- And, if we try to re-add that scope using bind(), it makes the return value of .toString() unreadable, which means we cannot detect duplicate listeners.
|
||||||
- Therefore, we just have to attach the navigate event to the element, and manually trigger that when the window listener fires.
|
- Therefore, we attach a global navigate event to the window, and each navigate event in this array, and manually trigger each event when the global one fires.
|
||||||
*/
|
*/
|
||||||
navigateListeners = []
|
navigateListeners = []
|
||||||
HTMLElement.prototype.onNavigate = function(cb) {
|
|
||||||
this._storeListener("navigate", cb);
|
|
||||||
|
|
||||||
let found = false
|
|
||||||
for(entry of navigateListeners) {
|
|
||||||
if(entry.cb.toString() === cb.toString() &&
|
|
||||||
this.nodeName === entry.el.nodeName) {
|
|
||||||
found = true
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(found === false) {
|
|
||||||
navigateListeners.push({el: this, cb: cb})
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
window.addEventListener("navigate", () => {
|
window.addEventListener("navigate", () => {
|
||||||
for(entry of navigateListeners) {
|
for(entry of navigateListeners) {
|
||||||
entry.el.dispatchEvent(new CustomEvent("navigate"))
|
entry.el.dispatchEvent(new CustomEvent("navigate"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
HTMLElement.prototype.onNavigate = function(cb) {
|
||||||
|
this._storeListener("navigate", cb);
|
||||||
|
|
||||||
|
let found = false
|
||||||
|
let elementIndex = Array.from(this.parentNode.children).indexOf(this)
|
||||||
|
for(entry of navigateListeners) {
|
||||||
|
if(
|
||||||
|
entry.cb.toString() === cb.toString()
|
||||||
|
&& entry.index === elementIndex
|
||||||
|
&& this.nodeName === entry.el.nodeName
|
||||||
|
) {
|
||||||
|
found = true
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(found === false) {
|
||||||
|
navigateListeners.push({el: this, cb: cb, index: elementIndex})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Same principle applies
|
Same principle applies
|
||||||
|
|||||||
Reference in New Issue
Block a user