@surface/custom-element
Version:
Provides support of directives and data binding on custom elements.
78 lines (77 loc) • 2.56 kB
JavaScript
import { enumerateRange } from "../common.js";
import { disposeTree } from "../singletons.js";
const BLOCKS = Symbol("custom-element:block");
export default class Block {
constructor() {
this.disposed = false;
this.start = document.createComment("#start");
this.end = document.createComment("#end");
this.start[BLOCKS] = new Set([this]);
this.end[BLOCKS] = new Set([this]);
}
isAnchor(node) {
return !!node[BLOCKS];
}
optimize() {
const hasNestedDirective = this.start.nextSibling
&& this.start.nextSibling != this.end
&& this.isAnchor(this.start.nextSibling)
&& this.end.previousSibling
&& this.end.previousSibling != this.start
&& this.isAnchor(this.end.previousSibling);
if (hasNestedDirective) {
const nextOpen = this.start.nextSibling;
const previousClose = this.end.previousSibling;
const open = this.start;
const close = this.end;
for (const block of open[BLOCKS].values()) {
block.start = nextOpen;
nextOpen[BLOCKS].add(block);
}
for (const block of close[BLOCKS].values()) {
block.end = previousClose;
previousClose[BLOCKS].add(block);
}
open.remove();
close.remove();
}
}
connect(node) {
node.appendChild(this.start);
node.appendChild(this.end);
}
clear() {
for (const element of enumerateRange(this.start, this.end)) {
element.remove();
disposeTree(element);
}
}
dispose() {
if (!this.disposed) {
this.clear();
if (this.start[BLOCKS].size == 1) {
this.start.remove();
}
else {
this.start[BLOCKS].delete(this);
}
if (this.end[BLOCKS].size == 1) {
this.end.remove();
}
else {
this.end[BLOCKS].delete(this);
}
this.disposed = true;
}
}
insertAt(parent, reference) {
parent.replaceChild(this.end, reference);
parent.insertBefore(this.start, this.end);
}
setContent(content, optimize = true) {
this.end.parentNode.insertBefore(content, this.end);
if (optimize) {
this.optimize();
}
}
}