parent
e36620854c
commit
a67e04bdc4
@ -0,0 +1,74 @@
|
|||||||
|
import { AbstractVAO, TMappingFn, VAO } from "./VAO.js";
|
||||||
|
import { RenderableVAO } from "./RenderableVAO.js";
|
||||||
|
import { VAOUtil } from "./VAOUtil.js";
|
||||||
|
|
||||||
|
// === TODO ===
|
||||||
|
// [A] yielded VAO should follow array VAO
|
||||||
|
// [B] consider if this way of passing detachable can be improved
|
||||||
|
|
||||||
|
const DIFF_INSERT = {};
|
||||||
|
const DIFF_REMOVE = {};
|
||||||
|
const DIFF_REPLACE = {};
|
||||||
|
|
||||||
|
export class ArrayVAO extends AbstractVAO {
|
||||||
|
private value_: any[]
|
||||||
|
|
||||||
|
protected getTopics_(): string[] {
|
||||||
|
return [
|
||||||
|
'change',
|
||||||
|
'item.insert',
|
||||||
|
'item.remove',
|
||||||
|
'item.replace'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor (initialValue: any[]) {
|
||||||
|
super();
|
||||||
|
if ( ! Array.isArray(initialValue) ) {
|
||||||
|
throw new Error('ArrayVAO expects an array');
|
||||||
|
}
|
||||||
|
this.value_ = initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move to AbstractVAO if this isn't modified by version 1.2.0
|
||||||
|
set (value: any[]) {
|
||||||
|
let o = this.value_;
|
||||||
|
this.value_ = value;
|
||||||
|
if ( o !== this.value_ ) {
|
||||||
|
this.pub('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get (): any {
|
||||||
|
return this.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
each (mappingFn: TMappingFn): VAO[] {
|
||||||
|
const results = [];
|
||||||
|
// TODO: [A]
|
||||||
|
for ( const item of this.value_ ) {
|
||||||
|
const vao = VAOUtil.getAppropriateVAO(item);
|
||||||
|
const [renderable, detachable] =
|
||||||
|
vao.map(mappingFn, RenderableVAO);
|
||||||
|
// TODO: [B]
|
||||||
|
;(renderable as RenderableVAO).detachable?.detach?.();
|
||||||
|
results.push(renderable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffArrays_(oldArray, newArray) {
|
||||||
|
// let ptrOld = 0;
|
||||||
|
// let ptrNew = 0;
|
||||||
|
|
||||||
|
// const events = [];
|
||||||
|
|
||||||
|
// while ( ptrOld < oldArray.length ) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// while ( otrNew < newArray.length ) {
|
||||||
|
// events.push();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
import { ScalarVAO } from "./ScalarVAO.js";
|
||||||
|
import { Detachable } from "./VAO.js";
|
||||||
|
import { Renderable, INotifyMounted } from "../component/componentdefs.js";
|
||||||
|
|
||||||
|
export class RenderableVAO extends ScalarVAO {
|
||||||
|
protected renderChildren_: Renderable[] | null = null;
|
||||||
|
protected elements_: Element[];
|
||||||
|
protected parent_: Element | null = null;
|
||||||
|
protected rerenderSubscription_: Detachable | null;
|
||||||
|
|
||||||
|
public detachable: Detachable | null = null;
|
||||||
|
|
||||||
|
constructor (value: any) {
|
||||||
|
super(value);
|
||||||
|
|
||||||
|
this.elements_ = [];
|
||||||
|
this.rerenderSubscription_ = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
createElements (): Element[] {
|
||||||
|
let renderables = this.get();
|
||||||
|
let renderablesTS: Renderable[] = Array.isArray(renderables)
|
||||||
|
? renderables : [renderables];
|
||||||
|
|
||||||
|
const elements = [];
|
||||||
|
for ( const renderable of renderablesTS ) {
|
||||||
|
const theseElements = renderable.createElements();
|
||||||
|
elements.push(...theseElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.elements_ = elements;
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
rerenderSelf () {
|
||||||
|
if ( ! this.parent_ ) {
|
||||||
|
throw new Error('cannot re-render; no existing element');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.elements_.length === 0 ) {
|
||||||
|
throw new Error('cannot re-render a component with zero elements');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onWillDetachFromDOM_();
|
||||||
|
|
||||||
|
// The reference element lets us know where to put new elements
|
||||||
|
const referenceElement = this.elements_[0];
|
||||||
|
|
||||||
|
// Remove following elements - we're going to replace the first one
|
||||||
|
for ( const element of this.elements_.slice(1) ) {
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const newElements = this.createElements();
|
||||||
|
referenceElement.replaceWith(...newElements);
|
||||||
|
this.notifyMounted({
|
||||||
|
parent: this.parent_,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyWillDetachFromDOM () {
|
||||||
|
if ( this.detachable ) this.detachable.detach();
|
||||||
|
this.onWillDetachFromDOM_();
|
||||||
|
}
|
||||||
|
onWillDetachFromDOM_ () {
|
||||||
|
if ( this.rerenderSubscription_ ) {
|
||||||
|
this.rerenderSubscription_.detach();
|
||||||
|
}
|
||||||
|
// Detach listeners from child renderables generated last time
|
||||||
|
if ( ! this.renderChildren_ ) {
|
||||||
|
throw new Error('tried to detach before rendering');
|
||||||
|
}
|
||||||
|
for ( const renderable of this.renderChildren_ ) {
|
||||||
|
renderable.notifyWillDetachFromDOM();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyMounted(event: INotifyMounted) {
|
||||||
|
this.parent_ = event.parent;
|
||||||
|
|
||||||
|
this.rerenderSubscription_ = this.sub('change', () => {
|
||||||
|
this.rerenderSelf();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { ScalarVAO } from "./ScalarVAO.js";
|
||||||
|
import { ArrayVAO } from "./ArrayVAO.js";
|
||||||
|
import { AbstractVAO, VAO } from "./VAO.js";
|
||||||
|
|
||||||
|
export class VAOUtil {
|
||||||
|
static getAppropriateVAO (value: any): VAO {
|
||||||
|
if ( value instanceof AbstractVAO ) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(value) ) {
|
||||||
|
return new ArrayVAO(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ScalarVAO(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in new issue