rewrite array reactivity entirely and fix if statemensts also add fragemetns lol

This commit is contained in:
CoolElectronics 2024-02-23 10:54:18 -05:00
parent 21a6b460a7
commit a3aff2f248
No known key found for this signature in database
GPG key ID: F63593D168636C50
4 changed files with 42 additions and 30 deletions

5
AliceJS.d.ts vendored
View file

@ -2,7 +2,7 @@ declare namespace JSX {
export type IntrinsicElements = { export type IntrinsicElements = {
[index: string]: any [index: string]: any
}; };
type ElementType = string | Component<any, any>; type ElementType = Fragment | string | Component<any, any>;
type Element = DLElement<any>; type Element = DLElement<any>;
interface ElementAttributesProperty { interface ElementAttributesProperty {
@ -42,6 +42,9 @@ type DLCSS = string;
declare var $el: HTMLElement; declare var $el: HTMLElement;
type Fragment = { readonly fragment: unique symbol };
declare var Fragment: Fragment;
interface Element { interface Element {
$: OuterComponentTypes & { [index: string | symbol]: any } $: OuterComponentTypes & { [index: string | symbol]: any }
} }

View file

@ -59,7 +59,7 @@ To get started with AliceJS, add this to the compileroptions of your `tsconfig.j
```json ```json
"jsx":"react", "jsx":"react",
"jsxFactory":"h", "jsxFactory":"h",
"jsxFragmentFactory":"YOU_CANT_USE_FRAGMENTS", "jsxFragmentFactory":"Fragment",
"types": ["@mercuryworkshop/alicejs"], "types": ["@mercuryworkshop/alicejs"],
``` ```
and run `npm install @mercuryworkshop/alicejs` and run `npm install @mercuryworkshop/alicejs`

63
js.js
View file

@ -1,3 +1,5 @@
export const Fragment = Symbol();
// whether to return the true value from a stateful object or a "trap" containing the pointer // whether to return the true value from a stateful object or a "trap" containing the pointer
let __use_trap = false; let __use_trap = false;
@ -30,13 +32,14 @@ Object.defineProperty(window, "use", {
}; };
} }
}); });
Object.assign(window, { isDLPtr, h, stateful, handle, useValue, $if }); Object.assign(window, { isDLPtr, h, stateful, handle, useValue, $if, Fragment });
const TARGET = Symbol(); const TARGET = Symbol();
const PROXY = Symbol(); const PROXY = Symbol();
const STEPS = Symbol(); const STEPS = Symbol();
const LISTENERS = Symbol(); const LISTENERS = Symbol();
const IF = Symbol();
const TRAPS = new Map; const TRAPS = new Map;
// This wraps the target in a proxy, doing 2 things: // This wraps the target in a proxy, doing 2 things:
// - whenever a property is accessed, return a "trap" that catches and records accessors // - whenever a property is accessed, return a "trap" that catches and records accessors
@ -90,18 +93,8 @@ export function $if(condition, then, otherwise) {
otherwise ??= document.createTextNode(""); otherwise ??= document.createTextNode("");
then ??= document.createTextNode(""); then ??= document.createTextNode("");
if (!isDLPtr(condition)) return condition ? then : otherwise; if (!isDLPtr(condition)) return condition ? then : otherwise;
let root = then;
handle(condition, v => {
if (v) {
root.replaceWith(then);
root = then;
} else {
root.replaceWith(otherwise);
root = otherwise;
}
})
return root; return { [IF]: condition, then, otherwise };
} }
// This lets you subscribe to a stateful object // This lets you subscribe to a stateful object
@ -164,7 +157,9 @@ export function useValue(references) {
// Actual JSX factory. Responsible for creating the HTML elements and all of the *reactive* syntactic sugar // Actual JSX factory. Responsible for creating the HTML elements and all of the *reactive* syntactic sugar
export function h(type, props, ...children) { export function h(type, props, ...children) {
if (type === Fragment) return children;
if (typeof type === "function") { if (typeof type === "function") {
// functional components. create the stateful object
let newthis = stateful(Object.create(type.prototype)); let newthis = stateful(Object.create(type.prototype));
for (const name in props) { for (const name in props) {
@ -222,8 +217,26 @@ export function h(type, props, ...children) {
let xmlns = props?.xmlns; let xmlns = props?.xmlns;
const elm = xmlns ? document.createElementNS(xmlns, type) : document.createElement(type); const elm = xmlns ? document.createElementNS(xmlns, type) : document.createElement(type);
for (const child of children) { for (const child of children) {
JSXAddChild(child, elm.appendChild.bind(elm)); let cond = !isDLPtr(child) && child[IF];
if (cond) {
let appended = null;
handle(cond, v => {
let before = appended?.[0]?.previousSibling;
if (appended)
appended.forEach(a => a.remove());
appended = JSXAddChild(v ? child.then : child.otherwise, el => {
if (before) {
before.after(el)
before = el;
}
else elm.appendChild(el)
})
})
} else
JSXAddChild(child, elm.appendChild.bind(elm));
} }
if (!props) return elm; if (!props) return elm;
@ -308,21 +321,16 @@ function JSXAddChild(child, cb) {
if (isDLPtr(child)) { if (isDLPtr(child)) {
let appended = []; let appended = [];
handle(child, (val) => { handle(child, (val) => {
if (appended.length > 1) { let v = appended[0]?.previousSibling;
// this is why we don't encourage arrays (jank) appended.forEach(n => n.remove());
appended.forEach(n => n.remove()); appended = JSXAddChild(val, el => {
appended = JSXAddChild(val, cb); if (v) {
} else if (appended.length > 0) { v.after(el);
let old = appended[0]; v = el;
appended = JSXAddChild(val, cb);
if (appended[0]) {
old.replaceWith(appended[0])
} else {
old.remove();
} }
} else { else
appended = JSXAddChild(val, cb); cb(el);
} });
}); });
} else if (child instanceof Node) { } else if (child instanceof Node) {
cb(child); cb(child);
@ -332,6 +340,7 @@ function JSXAddChild(child, cb) {
for (const childchild of child) { for (const childchild of child) {
elms = elms.concat(JSXAddChild(childchild, cb)); elms = elms.concat(JSXAddChild(childchild, cb));
} }
if (!elms[0]) elms = JSXAddChild("", cb);
return elms; return elms;
} else { } else {
let node = document.createTextNode(child); let node = document.createTextNode(child);

View file

@ -4,8 +4,8 @@ export function $store(target, ident, type) {
target = JSON.parse(stored) ?? target; target = JSON.parse(stored) ?? target;
addEventListener("beforeunload", () => { addEventListener("beforeunload", () => {
localStorage.setItem(JSON.stringify(target));
console.info("[dreamland.js]: saving " + ident); console.info("[dreamland.js]: saving " + ident);
localStorage.setItem(ident, JSON.stringify(target));
}); });
return stateful(target); return stateful(target);