diff --git a/dist/dev/json.js b/dist/dev/json.js index f55401d..f154571 100644 --- a/dist/dev/json.js +++ b/dist/dev/json.js @@ -21,6 +21,7 @@ var errors = { invalid_interpolation: (template, value) => new SyntaxError(`Invalid interpolation - expected hole or array: ${String(value)} found in template ${asTemplate(template)}`), invalid_hole: value => new SyntaxError(`Invalid interpolation - expected hole: ${String(value)}`), invalid_key: value => new SyntaxError(`Invalid key attribute or position in template: ${String(value)}`), + invalid_ref: template => new SyntaxError(`Invalid ref attribute or position in template: ${asTemplate(template)}`), invalid_array: value => new SyntaxError(`Invalid array - expected html/svg but found something else: ${String(value)}`), invalid_component: value => new SyntaxError(`Invalid component: ${String(value)}`), }; diff --git a/dist/dev/parser.js b/dist/dev/parser.js index 4163343..41b26b8 100644 --- a/dist/dev/parser.js +++ b/dist/dev/parser.js @@ -21,6 +21,7 @@ var errors = { invalid_interpolation: (template, value) => new SyntaxError(`Invalid interpolation - expected hole or array: ${String(value)} found in template ${asTemplate(template)}`), invalid_hole: value => new SyntaxError(`Invalid interpolation - expected hole: ${String(value)}`), invalid_key: value => new SyntaxError(`Invalid key attribute or position in template: ${String(value)}`), + invalid_ref: template => new SyntaxError(`Invalid ref attribute or position in template: ${asTemplate(template)}`), invalid_array: value => new SyntaxError(`Invalid array - expected html/svg but found something else: ${String(value)}`), invalid_component: value => new SyntaxError(`Invalid component: ${String(value)}`), }; diff --git a/src/dom/rabbit.js b/src/dom/rabbit.js index 1537497..7636acd 100644 --- a/src/dom/rabbit.js +++ b/src/dom/rabbit.js @@ -8,7 +8,7 @@ import { children } from './ish.js'; import { effect } from './signals.js'; import { isArray } from '../utils.js'; import { PersistentFragment, diffFragment, nodes } from './persistent-fragment.js'; -import { ARRAY, COMMENT, COMPONENT, EVENT, KEY, REF, SIGNAL, ref } from './update.js'; +import { ARRAY, COMMENT, COMPONENT, EVENT, KEY, REF, SIGNAL, HOLE, ref } from './update.js'; import { _get as getDirect, _set as setDirect } from './direct.js'; import { Signal, _get as getSignal, _set as setSignal } from './signals.js'; @@ -37,7 +37,7 @@ const component = (Component, obj, signals) => { const signal = getSignal(); const length = signals.length; let i = 0; - setSignal(/** @param {unknown} value */ value => i < length ? signals[i++] : (signals[i++] = signal(value))); + setSignal((value, options) => i < length ? signals[i++] : (signals[i++] = signal(value, options))); const wasDirect = getDirect(); if (wasDirect) setDirect(!wasDirect); try { return Component(obj, global); } @@ -64,6 +64,30 @@ const getHole = (hole, value) => { return hole; }; +const notifyRefs = (refs, template) => { + for (const node of refs) { + const value = node[ref]; + switch (typeof value) { + case 'object': { + if (value instanceof Signal) + value.value = node; + else { + //@ts-ignore + if (DEBUG && !value) throw errors.invalid_ref(template); + value.current = node; + } + break; + } + case 'function': { + value(node); + break; + } + //@ts-ignore + default: if (DEBUG) throw errors.invalid_ref(template); + } + } +}; + const props = Symbol(); const global = {}; @@ -143,20 +167,25 @@ export class Hole { } } else { - let commit = true; + let t = type, commit = true, isComment = type & COMMENT; //@ts-ignore if (DEBUG && (type & ARRAY) && !isArray(value)) throw errors.invalid_interpolation(this.t[3], value); - if (!direct && (type & COMMENT) && !(type & SIGNAL)) { + if (!direct && isComment && !(type & SIGNAL)) { if (type & ARRAY) { commit = false; //@ts-ignore if (value.length) { //@ts-ignore - node.before(...(node[nodes] = value[0] instanceof Hole ? value.map(dom) : value)); + const isHole = value[0] instanceof Hole; + if (isHole) t |= HOLE; + //@ts-ignore + node.before(...(node[nodes] = isHole ? value.map(dom) : value)); } } else if (value instanceof Hole) { + t |= HOLE; commit = false; + //@ts-ignore update(node, dom(value)); } } @@ -171,21 +200,12 @@ export class Hole { } } //@ts-ignore - changes[length] = [type, update, value, node]; - if (direct && (type & COMMENT)) node.remove(); - } - } - if (refs) { - for (const node of refs) { - const value = node[ref]; - if (typeof value === 'function') - value(node); - else if (value instanceof Signal) - value.value = node; - else if (value) - value.current = node; + changes[length] = [t, update, value, node]; + if (direct && isComment) node.remove(); } } + //@ts-ignore + if (refs) notifyRefs(refs, DEBUG && this.t[3]); } const { childNodes } = root; @@ -235,16 +255,16 @@ export class Hole { else update(prev, value); } else { - let change = value; + let change = value, isComment = type & COMMENT, isSignal = type & SIGNAL, isHole = type & HOLE; if (type & ARRAY) { if (DEBUG && !isArray(value)) throw errors.invalid_interpolation([], value); - if (type & COMMENT) { + if (isComment) { //@ts-ignore const l = value.length; const h = prev.length; // TODO: a smarter differ that does not require 2 loops //@ts-ignore - if (l && (value[0] instanceof Hole)) { + if (l && (isHole || (value[0] instanceof Hole && (entry[0] |= HOLE)))) { //@ts-ignore if (DEBUG && h && !(prev[0] instanceof Hole)) throw errors.invalid_interpolation([], value[0]); change = []; @@ -260,21 +280,13 @@ export class Hole { //@ts-ignore else if ((type & EVENT) && (value[0] === prev[0])) continue; } - else if (type & COMMENT) { - if (type & SIGNAL) { - if (value === prev) { - update(entry[3], change); - continue; - } - } - else if (prev instanceof Hole) { - if (DEBUG && !(value instanceof Hole)) throw errors.invalid_interpolation([], value); - value = getHole(prev, /** @type {Hole} */(value)); - //@ts-ignore - change = value.n; - } + else if (isHole && !isSignal) { + if (DEBUG && !(value instanceof Hole)) throw errors.invalid_interpolation([], value); + value = getHole(prev, /** @type {Hole} */(value)); + //@ts-ignore + change = value.n; } - if (value !== prev) { + if (isSignal || value !== prev) { entry[2] = value; update(entry[3], change); } diff --git a/src/dom/update.js b/src/dom/update.js index 5928a4d..7a55263 100644 --- a/src/dom/update.js +++ b/src/dom/update.js @@ -31,6 +31,7 @@ export const TOGGLE = 1 << 12; export const UNSAFE = 1 << 13; export const REF = 1 << 14; export const SIGNAL = 1 << 15; +export const HOLE = 1 << 16; // COMPONENT flags const COMPONENT_DIRECT = COMPONENT | DIRECT; diff --git a/src/errors.js b/src/errors.js index 6821de1..3060e48 100644 --- a/src/errors.js +++ b/src/errors.js @@ -21,6 +21,7 @@ export default { invalid_interpolation: (template, value) => new SyntaxError(`Invalid interpolation - expected hole or array: ${String(value)} found in template ${asTemplate(template)}`), invalid_hole: value => new SyntaxError(`Invalid interpolation - expected hole: ${String(value)}`), invalid_key: value => new SyntaxError(`Invalid key attribute or position in template: ${String(value)}`), + invalid_ref: template => new SyntaxError(`Invalid ref attribute or position in template: ${asTemplate(template)}`), invalid_array: value => new SyntaxError(`Invalid array - expected html/svg but found something else: ${String(value)}`), invalid_component: value => new SyntaxError(`Invalid component: ${String(value)}`), }; diff --git a/types/dom/update.d.ts b/types/dom/update.d.ts index e3c9159..c421fa2 100644 --- a/types/dom/update.d.ts +++ b/types/dom/update.d.ts @@ -14,6 +14,7 @@ export const TOGGLE: number; export const UNSAFE: number; export const REF: number; export const SIGNAL: number; +export const HOLE: number; export const fragment: (content: string, xml?: boolean) => DocumentFragment; export const ref: unique symbol; export function isKeyed(): boolean; diff --git a/types/errors.d.ts b/types/errors.d.ts index 62bb153..480f9d4 100644 --- a/types/errors.d.ts +++ b/types/errors.d.ts @@ -14,6 +14,7 @@ declare namespace _default { function invalid_interpolation(template: any, value: any): SyntaxError; function invalid_hole(value: any): SyntaxError; function invalid_key(value: any): SyntaxError; + function invalid_ref(template: any): SyntaxError; function invalid_array(value: any): SyntaxError; function invalid_component(value: any): SyntaxError; }