typed-dom
Advanced tools
Comparing version 0.0.281 to 0.0.282
{ | ||
"name": "typed-dom", | ||
"version": "0.0.281", | ||
"version": "0.0.282", | ||
"description": "A value-level and type-level DOM builder.", | ||
@@ -5,0 +5,0 @@ "private": false, |
143
src/proxy.ts
@@ -64,4 +64,6 @@ import { Event } from 'spica/global'; | ||
namespace privates { | ||
export const listeners = Symbol.for('typed-dom::listeners'); | ||
// All the properties accessed from its parent must avoid conflicts with | ||
// the same properties of custom proxies. | ||
namespace publics { | ||
export const events = Symbol.for('typed-dom::events'); | ||
} | ||
@@ -83,8 +85,3 @@ | ||
) { | ||
const listeners = this[privates.listeners]; | ||
assert('' != null); | ||
listeners.mutate = 'onmutate' in element && element['onmutate'] != null; | ||
listeners.connect = 'onconnect' in element && element['onconnect'] != null; | ||
listeners.disconnect = 'ondisconnect' in element && element['ondisconnect'] != null; | ||
this.children_ = children; | ||
this.$children = children; | ||
this.container = container; | ||
@@ -118,3 +115,3 @@ switch (true) { | ||
case ElChildType.Array: | ||
this.children_ = [] as El.Children.Array as C; | ||
this.$children = [] as El.Children.Array as C; | ||
this.children = children as El.Setter<C>; | ||
@@ -124,3 +121,3 @@ this.isInit = false; | ||
case ElChildType.Struct: | ||
this.children_ = this.observe(children as El.Children.Struct) as C; | ||
this.$children = this.observe(children as El.Children.Struct) as C; | ||
this.children = children as El.Setter<C>; | ||
@@ -133,20 +130,7 @@ this.isInit = false; | ||
} | ||
private [privates.listeners] = { | ||
mutate: false, | ||
connect: false, | ||
disconnect: false, | ||
values: [] as El[], | ||
add(child: El): void { | ||
this.values.push(child); | ||
}, | ||
delete(child: El): void { | ||
assert(this.values.indexOf(child) > -1); | ||
splice(this.values, this.values.indexOf(child), 1); | ||
}, | ||
}; | ||
private id_ = ''; | ||
private $id = ''; | ||
private get id(): string { | ||
if (this.id_) return this.id_; | ||
this.id_ = this.element.id; | ||
if (/^[a-z][\w-]*$/i.test(this.id_)) return this.id_; | ||
if (this.$id) return this.$id; | ||
this.$id = this.element.id; | ||
if (/^[a-z][\w-]*$/i.test(this.$id)) return this.$id; | ||
if (counter === 999) { | ||
@@ -156,17 +140,17 @@ id = identity(); | ||
} | ||
this.id_ = `rnd-${id}-${++counter}`; | ||
assert(!this.element.classList.contains(this.id_)); | ||
this.element.classList.add(this.id_); | ||
return this.id_; | ||
this.$id = `rnd-${id}-${++counter}`; | ||
assert(!this.element.classList.contains(this.$id)); | ||
this.element.classList.add(this.$id); | ||
return this.$id; | ||
} | ||
private query_ = ''; | ||
private $query = ''; | ||
private get query(): string { | ||
if (this.query_) return this.query_; | ||
if (this.$query) return this.$query; | ||
switch (true) { | ||
case this.element !== this.container: | ||
return this.query_ = ':host'; | ||
return this.$query = ':host'; | ||
case this.id === this.element.id: | ||
return this.query_ = `#${this.id}`; | ||
return this.$query = `#${this.id}`; | ||
default: | ||
return this.query_ = `.${this.id}`; | ||
return this.$query = `.${this.id}`; | ||
} | ||
@@ -180,3 +164,3 @@ } | ||
const style = source.replace(scope, (...$) => `${$[1]}${$[2]}${this.query}`); | ||
assert(!this.query_ || style !== source); | ||
assert(!this.$query || style !== source); | ||
if (style === source) return; | ||
@@ -215,3 +199,4 @@ child.element.innerHTML = style; | ||
private isInit = true; | ||
private children_: C; | ||
private $children: C; | ||
public readonly [publics.events] = new Events(this.element); | ||
public get children(): El.Getter<C> { | ||
@@ -222,3 +207,3 @@ switch (this.type) { | ||
default: | ||
return this.children_ as El.Getter<C>; | ||
return this.$children as El.Getter<C>; | ||
} | ||
@@ -236,3 +221,3 @@ } | ||
case ElChildType.Text: { | ||
if (this.isInit || !this[privates.listeners].mutate) { | ||
if (this.isInit || !this[publics.events].mutate) { | ||
container.textContent = children as El.Children.Text; | ||
@@ -251,3 +236,3 @@ isMutated = true; | ||
const sourceChildren = children as El.Children.Array; | ||
const targetChildren = this.children_ as El.Children.Array; | ||
const targetChildren = this.$children as El.Children.Array; | ||
isMutated ||= sourceChildren.length !== targetChildren.length; | ||
@@ -262,3 +247,3 @@ for (let i = 0; i < sourceChildren.length; ++i) { | ||
assert(!addedChildren.includes(newChild)); | ||
hasListener(newChild) && addedChildren.push(newChild) && this[privates.listeners].add(newChild); | ||
hasListener(newChild) && addedChildren.push(newChild) && this[publics.events].add(newChild); | ||
} | ||
@@ -274,3 +259,3 @@ } | ||
} | ||
this.children_ = sourceChildren as C; | ||
this.$children = sourceChildren as C; | ||
for (let i = 0; i < targetChildren.length; ++i) { | ||
@@ -280,3 +265,3 @@ const oldChild = targetChildren[i]; | ||
assert(!removedChildren.includes(oldChild)); | ||
hasListener(oldChild) && removedChildren.push(oldChild) && this[privates.listeners].delete(oldChild); | ||
hasListener(oldChild) && removedChildren.push(oldChild) && this[publics.events].del(oldChild); | ||
assert(isMutated); | ||
@@ -300,3 +285,3 @@ } | ||
assert(!addedChildren.includes(newChild)); | ||
hasListener(newChild) && addedChildren.push(newChild) && this[privates.listeners].add(newChild); | ||
hasListener(newChild) && addedChildren.push(newChild) && this[publics.events].add(newChild); | ||
isMutated = true; | ||
@@ -307,3 +292,3 @@ } | ||
const sourceChildren = children as El.Children.Struct; | ||
const targetChildren = this.children_ as El.Children.Struct; | ||
const targetChildren = this.$children as El.Children.Struct; | ||
if (sourceChildren === targetChildren) break; | ||
@@ -322,5 +307,5 @@ for (const name in sourceChildren) { | ||
assert(!addedChildren.includes(newChild)); | ||
hasListener(newChild) && addedChildren.push(newChild) && this[privates.listeners].add(newChild); | ||
hasListener(newChild) && addedChildren.push(newChild) && this[publics.events].add(newChild); | ||
assert(!removedChildren.includes(oldChild)); | ||
hasListener(oldChild) && removedChildren.push(oldChild) && this[privates.listeners].delete(oldChild); | ||
hasListener(oldChild) && removedChildren.push(oldChild) && this[publics.events].del(oldChild); | ||
} | ||
@@ -342,6 +327,6 @@ else { | ||
} | ||
this.dispatchDisconnectionEvent(removedChildren); | ||
this.dispatchConnectionEvent(addedChildren); | ||
this.dispatchDisconnectEvent(removedChildren); | ||
this.dispatchConnectEvent(addedChildren); | ||
assert(isMutated || removedChildren.length + addedChildren.length === 0); | ||
if (isMutated && this[privates.listeners].mutate) { | ||
if (isMutated && this[publics.events].mutate) { | ||
this.element.dispatchEvent(new Event('mutate', { bubbles: false, cancelable: true })); | ||
@@ -353,20 +338,20 @@ } | ||
} | ||
private dispatchConnectionEvent( | ||
listeners: El[] | undefined = this[privates.listeners].values, | ||
private dispatchConnectEvent( | ||
listeners: El[] | undefined = this[publics.events].listeners, | ||
): void { | ||
if (listeners.length === 0) return; | ||
if (listeners !== this[privates.listeners].values && !this.isConnected) return; | ||
if (listeners !== this[publics.events].listeners && !this.isConnected) return; | ||
for (const listener of listeners) { | ||
listener.element[proxy].dispatchConnectionEvent(); | ||
getListeners(listener)?.connect && listener.element.dispatchEvent(new Event('connect', { bubbles: false, cancelable: true })); | ||
(listener.element[proxy] as this).dispatchConnectEvent(); | ||
getEvents(listener).connect && listener.element.dispatchEvent(new Event('connect', { bubbles: false, cancelable: true })); | ||
} | ||
} | ||
private dispatchDisconnectionEvent( | ||
listeners: El[] | undefined = this[privates.listeners].values, | ||
private dispatchDisconnectEvent( | ||
listeners: El[] | undefined = this[publics.events].listeners, | ||
): void { | ||
if (listeners.length === 0) return; | ||
if (listeners !== this[privates.listeners].values && !this.isConnected) return; | ||
if (listeners !== this[publics.events].listeners && !this.isConnected) return; | ||
for (const listener of listeners) { | ||
listener.element[proxy].dispatchDisconnectionEvent(); | ||
getListeners(listener)?.disconnect && listener.element.dispatchEvent(new Event('disconnect', { bubbles: false, cancelable: true })); | ||
(listener.element[proxy] as this).dispatchDisconnectEvent(); | ||
getEvents(listener).disconnect && listener.element.dispatchEvent(new Event('disconnect', { bubbles: false, cancelable: true })); | ||
} | ||
@@ -376,8 +361,36 @@ } | ||
class Events { | ||
constructor( | ||
private readonly element: Element, | ||
) { | ||
} | ||
public get mutate(): boolean { | ||
return 'onmutate' in this.element | ||
&& this.element['onmutate'] != null; | ||
} | ||
public get connect(): boolean { | ||
return 'onconnect' in this.element | ||
&& this.element['onconnect'] != null; | ||
} | ||
public get disconnect(): boolean { | ||
return 'ondisconnect' in this.element | ||
&& this.element['ondisconnect'] != null; | ||
} | ||
public readonly listeners: El[] = []; | ||
public add(child: El): void { | ||
assert(this.listeners.indexOf(child) === -1); | ||
this.listeners.push(child); | ||
} | ||
public del(child: El): void { | ||
assert(this.listeners.indexOf(child) !== -1); | ||
splice(this.listeners, this.listeners.indexOf(child), 1); | ||
} | ||
} | ||
function hasListener(child: El): boolean { | ||
const ls = getListeners(child); | ||
return ls?.connect || ls?.disconnect || ls?.values.length! > 0; | ||
const events = getEvents(child); | ||
return events.connect || events.disconnect || events.listeners.length > 0; | ||
} | ||
function getListeners(child: El): ElementProxy[typeof privates.listeners] | undefined { | ||
return child[privates.listeners] ?? child.element[proxy]?.[privates.listeners]; | ||
function getEvents(child: El): Events { | ||
return child[publics.events] ?? (child.element[proxy] as ElementProxy)[publics.events]; | ||
} | ||
@@ -384,0 +397,0 @@ |
@@ -375,5 +375,9 @@ import { Shadow, HTML, SVG, El, Attrs, shadow, html } from '../..'; | ||
'a'); | ||
assert.deepStrictEqual({ ...dom.element }, {}); | ||
assert(dom.element['onmutate'] === ''); | ||
assert(dom.children === 'aa'); | ||
dom.children = 'b'; | ||
assert(dom.children === 'bb'); | ||
dom.element['onmutate'] = null; | ||
assert(dom.element['onmutate'] === null); | ||
}); | ||
@@ -392,2 +396,4 @@ | ||
]); | ||
assert(dom.element['onconnect'] === undefined); | ||
assert(dom.element['ondisconnect'] === undefined); | ||
assert.deepStrictEqual( | ||
@@ -394,0 +400,0 @@ dom.children.map(child => child.children), |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
698314
16371