feat: performance improvements, bindings to refs can be unsubbed
This commit is contained in:
@@ -35,11 +35,15 @@ export default class BeatView extends UINode implements ISubscriber<EventTypeSub
|
||||
this.setBeat(options.beat);
|
||||
}
|
||||
|
||||
setBeat(beat: Beat): void {
|
||||
this.beat = beat;
|
||||
this.sub?.unbind();
|
||||
this.sub = this.beat.addSubscriber(this, EventTypeSubscriptions);
|
||||
this.redraw();
|
||||
setBeat(beat: Beat | null): void {
|
||||
if (beat) {
|
||||
this.beat = beat;
|
||||
this.sub?.unbind();
|
||||
this.sub = this.beat.addSubscriber(this, EventTypeSubscriptions);
|
||||
this.redraw();
|
||||
} else {
|
||||
this.sub?.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
notify(publisher: unknown, event: EventTypeSubscriptions): void {
|
||||
@@ -58,7 +62,6 @@ export default class BeatView extends UINode implements ISubscriber<EventTypeSub
|
||||
|
||||
private rebuildBeatUnitViews() {
|
||||
const beatUnitCount = this.beat.getBarCount() * this.beat.getTimeSigUp();
|
||||
this.beatUnitViews.splice(beatUnitCount, this.beatUnitViews.length - beatUnitCount);
|
||||
for (let i = 0; i < beatUnitCount; i++) {
|
||||
const beatUnit = this.beat.getUnitByIndex(i);
|
||||
if (beatUnit) {
|
||||
@@ -74,6 +77,8 @@ export default class BeatView extends UINode implements ISubscriber<EventTypeSub
|
||||
}
|
||||
}
|
||||
}
|
||||
const deadViews = this.beatUnitViews.splice(beatUnitCount, this.beatUnitViews.length - beatUnitCount);
|
||||
deadViews.forEach(beatUnitView => beatUnitView.setUnit(null));
|
||||
}
|
||||
|
||||
private onBeatUnitClick(button: number, index: number) {
|
||||
|
||||
@@ -40,11 +40,18 @@ export default class BeatGroupView extends UINode implements ISubscriber<EventTy
|
||||
}
|
||||
|
||||
private setupBeatViews(): void {
|
||||
this.beatViews = [];
|
||||
for (let i = 0; i < this.beatGroup.getBeatCount(); i++) {
|
||||
this.beatViews.unshift(new BeatView({beat: this.beatGroup.getBeatByIndex(i)}));
|
||||
const newCount = this.beatGroup.getBeatCount();
|
||||
for (let i = 0; i < newCount; i++) {
|
||||
const beat = this.beatGroup.getBeatByIndex(i);
|
||||
if (beat && this.beatViews[i]) {
|
||||
this.beatViews[i].setBeat(beat);
|
||||
} else {
|
||||
this.beatViews.push(new BeatView({beat: this.beatGroup.getBeatByIndex(i)}));
|
||||
}
|
||||
}
|
||||
if (this.currentOrientation === "vertical") {
|
||||
const deadBeatViews = this.beatViews.splice(newCount, this.beatViews.length - newCount);
|
||||
deadBeatViews.forEach(beatView => beatView.setBeat(null));
|
||||
if (this.currentOrientation === "horizontal") {
|
||||
this.reverseDisplayOrder();
|
||||
}
|
||||
}
|
||||
@@ -57,16 +64,16 @@ export default class BeatGroupView extends UINode implements ISubscriber<EventTy
|
||||
}
|
||||
|
||||
private reverseDisplayOrder(): void {
|
||||
this.beatViews = this.beatViews.reverse();
|
||||
this.beatViews.reverse();
|
||||
this.getNode().classList.toggle("vertical");
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
setBeatGroup(newBeatGroup: BeatGroup): void {
|
||||
this.subscription.unbind();
|
||||
this.beatGroup = newBeatGroup;
|
||||
this.subscription.unbind();
|
||||
this.subscription = this.beatGroup.addSubscriber(this, BeatGroupEvents.BeatListChanged);
|
||||
this.beatViews.forEach((beatView, i) => beatView.setBeat(this.beatGroup.getBeatByIndex(i)));
|
||||
this.setupBeatViews();
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,28 @@ export default class BeatUnitView extends UINode implements ISubscriber<EventTyp
|
||||
this.setupBindings();
|
||||
}
|
||||
|
||||
setUnit(beatUnit: BeatUnit): void {
|
||||
this.beatUnit = beatUnit;
|
||||
this.setupBindings();
|
||||
this.notify(this.publisher, beatUnit.isOn() ? BeatUnitEvent.On : BeatUnitEvent.Off);
|
||||
this.notify(this.publisher, BeatUnitEvent.TypeChange);
|
||||
setUnit(beatUnit: BeatUnit | null): void {
|
||||
if (beatUnit) {
|
||||
this.beatUnit = beatUnit;
|
||||
this.setupBindings();
|
||||
this.notify(this.publisher, beatUnit.isOn() ? BeatUnitEvent.On : BeatUnitEvent.Off);
|
||||
this.notify(this.publisher, BeatUnitEvent.TypeChange);
|
||||
} else {
|
||||
this.subscription?.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
private setupBindings() {
|
||||
this.subscription?.unbind();
|
||||
this.subscription = this.beatUnit.addSubscriber(this, EventTypeSubscriptions);
|
||||
this.mouseDownListeners.forEach(listener => this.getNode().removeEventListener("mousedown", listener));
|
||||
this.hoverListeners.forEach(listener => this.getNode().removeEventListener("mouseover", listener));
|
||||
this.redraw();
|
||||
this.mouseDownListeners.forEach(listener => this.getNode().addEventListener("mousedown", listener));
|
||||
this.hoverListeners.forEach(listener => this.getNode().addEventListener("mouseover", listener));
|
||||
this.getNode().addEventListener("mousedown", (ev) => this.handleMouseDown(ev));
|
||||
this.getNode().addEventListener("touchstart", (ev) => this.handleTouchStart(ev));
|
||||
this.getNode().addEventListener("touchend", (ev) => this.handleTouchEnd(ev));
|
||||
}
|
||||
|
||||
private handleMouseDown(ev: MouseEvent): void {
|
||||
@@ -56,19 +73,6 @@ export default class BeatUnitView extends UINode implements ISubscriber<EventTyp
|
||||
}
|
||||
}
|
||||
|
||||
private setupBindings() {
|
||||
this.subscription?.unbind();
|
||||
this.subscription = this.beatUnit.addSubscriber(this, EventTypeSubscriptions);
|
||||
this.mouseDownListeners.forEach(listener => this.getNode().removeEventListener("mousedown", listener));
|
||||
this.hoverListeners.forEach(listener => this.getNode().removeEventListener("mouseover", listener));
|
||||
this.redraw();
|
||||
this.mouseDownListeners.forEach(listener => this.getNode().addEventListener("mousedown", listener));
|
||||
this.hoverListeners.forEach(listener => this.getNode().addEventListener("mouseover", listener));
|
||||
this.getNode().addEventListener("mousedown", (ev) => this.handleMouseDown(ev));
|
||||
this.getNode().addEventListener("touchstart", (ev) => this.handleTouchStart(ev));
|
||||
this.getNode().addEventListener("touchend", (ev) => this.handleTouchEnd(ev));
|
||||
}
|
||||
|
||||
toggle(): void {
|
||||
this.beatUnit.toggle();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Ref from "@/Ref";
|
||||
import {ISubscription} from "@/Publisher";
|
||||
|
||||
export type UINodeOptions = {
|
||||
|
||||
@@ -52,38 +53,41 @@ export function h<
|
||||
T extends keyof HTMLElementTagNameMap>(
|
||||
type: T,
|
||||
attributes: IRenderAttributes<T>,
|
||||
subNodes?: (Node | UINode | Ref<any>)[],
|
||||
subNodes?: (Node | UINode | Ref)[],
|
||||
): HTMLElementTagNameMap[T] {
|
||||
const element = document.createElement(type);
|
||||
if (attributes) {
|
||||
for (const key in attributes) {
|
||||
if (key === "classes") {
|
||||
element.classList.add(...attributes[key]!);
|
||||
} else if (key === "saveTo") {
|
||||
attributes.saveTo!.val = element;
|
||||
} else {
|
||||
if (!Object.prototype.hasOwnProperty.call(attributes, key)) {
|
||||
continue;
|
||||
}
|
||||
if (key === "classes" && attributes.classes) {
|
||||
element.classList.add(...attributes.classes);
|
||||
} else if (key === "saveTo" && attributes.saveTo) {
|
||||
attributes.saveTo.val = element;
|
||||
} else if (Object.prototype.hasOwnProperty.call(attributes, key)) {
|
||||
const attribute = (attributes as any)[key];
|
||||
if (attribute instanceof Ref) {
|
||||
element[key as keyof HTMLElementTagNameMap[T]] = attribute.val;
|
||||
attribute.watch((newVal) => element[key as keyof HTMLElementTagNameMap[T]] = newVal);
|
||||
} else {
|
||||
element[key as keyof HTMLElementTagNameMap[T]] = attribute;
|
||||
if (attribute) {
|
||||
if (attribute instanceof Ref) {
|
||||
if (element.hasAttribute(key)) {
|
||||
element.setAttribute(key, attribute.val);
|
||||
attribute.watch((newVal) => {
|
||||
if (element.hasAttribute(key)) {
|
||||
element.setAttribute(key, newVal);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (element.hasAttribute(key)) {
|
||||
element.setAttribute(key, attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (subNodes) {
|
||||
for (let i = 0; i < subNodes.length; i++) {
|
||||
const subNode = subNodes[i];
|
||||
if (subNode instanceof UINode) {
|
||||
element.append(subNode.render());
|
||||
} else if (subNode instanceof Ref) {
|
||||
subNode.watch((newVal) => element.childNodes.item(i).replaceWith(newVal.toString()));
|
||||
element.append(q(subNode.val.toString()));
|
||||
} else {
|
||||
element.append(subNode);
|
||||
}
|
||||
}
|
||||
attachSubs(element, subNodes);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
@@ -95,7 +99,31 @@ export function q(text: string): Text {
|
||||
export function frag(subs?: Node[]): DocumentFragment {
|
||||
const frag = document.createDocumentFragment();
|
||||
if (subs) {
|
||||
frag.append(...subs);
|
||||
attachSubs(frag, subs);
|
||||
}
|
||||
return frag;
|
||||
}
|
||||
|
||||
function nodeRefWatcher<T>(newVal: T extends Ref<infer U> ? U : never, textNode: Text, sub: ISubscription): void {
|
||||
if (!textNode.parentNode) {
|
||||
sub.unbind();
|
||||
textNode.remove();
|
||||
} else {
|
||||
textNode.replaceWith(newVal?.toString() ?? "null");
|
||||
}
|
||||
}
|
||||
|
||||
function attachSubs(node: Element | DocumentFragment, subNodes: (Node | UINode | Ref)[]): void {
|
||||
for (let i = 0; i < subNodes.length; i++) {
|
||||
const subNode = subNodes[i];
|
||||
if (subNode instanceof UINode) {
|
||||
node.append(subNode.render());
|
||||
} else if (subNode instanceof Ref) {
|
||||
const textNode = q(subNode.val.toString());
|
||||
const sub = subNode.watch((newVal) => nodeRefWatcher<Ref>(newVal, textNode, sub));
|
||||
node.append(textNode);
|
||||
} else {
|
||||
node.append(subNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,12 +39,12 @@ export default class BoolBoxView extends UINode {
|
||||
build(): HTMLDivElement {
|
||||
this.labelElement = h("label", {
|
||||
classes: ["bool-box-label"],
|
||||
innerText: this.label,
|
||||
onclick: () => {
|
||||
this.onInput(!this.checkboxElement.checked);
|
||||
},
|
||||
});
|
||||
if (this.label !== null) {
|
||||
this.labelElement.innerText = this.label;
|
||||
this.labelElement.classList.add("visible");
|
||||
}
|
||||
this.checkboxElement = h("input", {
|
||||
|
||||
Reference in New Issue
Block a user