diff --git a/config.json b/config.json
index 49e6167..98be335 100644
--- a/config.json
+++ b/config.json
@@ -1,3 +1,4 @@
{
- "development": true
+ "development": true,
+ "baseUrl": "/drum-slayer"
}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 5aca93a..f210032 100644
--- a/public/index.html
+++ b/public/index.html
@@ -7,9 +7,9 @@
Drum Slayer
-
+
-
+
diff --git a/src/Beat.ts b/src/Beat.ts
index 0d01a4a..6bc8677 100644
--- a/src/Beat.ts
+++ b/src/Beat.ts
@@ -1,4 +1,4 @@
-import BeatUnit, {BeatUnitType} from "@/BeatUnit";
+import BeatUnit from "@/BeatUnit";
import {IPublisher, Publisher} from "@/Publisher";
import ISubscriber from "@/Subscriber";
import BeatLike from "@/BeatLike";
@@ -16,13 +16,13 @@ export type BeatInitOptions = {
};
export const enum BeatEvents {
- NewTimeSig="BE0",
- NewBarCount="BE1",
- NewName="BE2",
- DisplayTypeChanged="BE3",
- LoopLengthChanged="BE4",
- WantsRemoval="BE5",
- Baked="BE6",
+ NewTimeSig="be-0",
+ NewBarCount="be-1",
+ NewName="be-2",
+ DisplayTypeChanged="be-3",
+ LoopLengthChanged="be-4",
+ WantsRemoval="be-5",
+ Baked="be-6",
}
export default class Beat implements IPublisher, BeatLike {
@@ -60,7 +60,7 @@ export default class Beat implements IPublisher, BeatLike {
this.publisher.notifySubs(BeatEvents.DisplayTypeChanged);
}
- addSubscriber(subscriber: ISubscriber, eventType: BeatEvents | BeatEvents[] | "all"): { unbind: () => void } {
+ addSubscriber(subscriber: ISubscriber, eventType: BeatEvents | BeatEvents[]): { unbind: () => void } {
return this.publisher.addSubscriber(subscriber, eventType);
}
diff --git a/src/BeatGroup.ts b/src/BeatGroup.ts
index 8d85cc7..7b3f8b2 100644
--- a/src/BeatGroup.ts
+++ b/src/BeatGroup.ts
@@ -14,17 +14,25 @@ type BeatGroupInitOptions = {
};
export const enum BeatGroupEvents {
- BeatOrderChanged="BGE0",
- BeatListChanged="BGE1",
- BarCountChanged="BGE2",
- TimeSigUpChanged="BGE3",
- AutoBeatSettingsChanged="BGE4",
- LockingChanged="BGE5",
+ BeatOrderChanged="bge-0",
+ BeatListChanged="bge-1",
+ BarCountChanged="bge-2",
+ TimeSigUpChanged="bge-3",
+ AutoBeatSettingsChanged="bge-4",
+ LockingChanged="bge-5",
}
-export default class BeatGroup implements IPublisher, BeatLike, ISubscriber {
+type EventTypeSubscriptions =
+ | BeatEvents.LoopLengthChanged
+ | BeatEvents.DisplayTypeChanged
+ | BeatEvents.WantsRemoval
+ | BeatEvents.Baked;
+
+type EventTypePublications = BeatGroupEvents | BeatEvents;
+
+export default class BeatGroup implements IPublisher, BeatLike, ISubscriber {
private beats: Beat[] = [];
- private publisher: Publisher = new Publisher(this);
+ private publisher: Publisher = new Publisher(this);
private barCount: number;
private timeSigUp: number;
private globalLoopLength: number;
@@ -45,7 +53,7 @@ export default class BeatGroup implements IPublisher(publisher: IPublisher, event: "all" | T[] | T): void {
+ notify(publisher: unknown, event: EventTypeSubscriptions): void {
switch (event) {
case BeatEvents.LoopLengthChanged:
case BeatEvents.DisplayTypeChanged:
@@ -60,7 +68,7 @@ export default class BeatGroup implements IPublisher void } {
+ addSubscriber(subscriber: ISubscriber, eventType: SubscriptionEvent): { unbind: () => void } {
return this.publisher.addSubscriber(subscriber, eventType);
}
diff --git a/src/BeatUnit.ts b/src/BeatUnit.ts
index 54670c6..a8ac87c 100644
--- a/src/BeatUnit.ts
+++ b/src/BeatUnit.ts
@@ -1,28 +1,27 @@
-import {IPublisher, ISubscription, Publisher} from "./Publisher";
+import {IPublisher, Publisher} from "./Publisher";
import ISubscriber from "./Subscriber";
-export enum BeatUnitType {
- Normal,
- GhostNote,
- Accent,
+export const enum BeatUnitType {
+ Normal="but-0",
+ GhostNote="but-1",
+ Accent="but-2",
+}
+
+export const enum BeatUnitEvent {
+ Toggle="bue-0",
+ On="bue-1",
+ Off="bue-2",
+ TypeChange="bue-3",
}
-export const enum BeatUnitEvents {
- Toggle,
- On,
- Off,
- TypeChange,
-}
-
-
-export default class BeatUnit implements IPublisher {
+export default class BeatUnit implements IPublisher {
private static readonly TypeRotation = [
BeatUnitType.Normal,
BeatUnitType.GhostNote,
BeatUnitType.Accent,
] as const;
- private publisher: Publisher = new Publisher(this);
+ private publisher: Publisher = new Publisher(this);
private on = false;
private typeIndex = 0;
@@ -31,28 +30,28 @@ export default class BeatUnit implements IPublisher {
this.setType(type);
}
- addSubscriber(subscriber: ISubscriber, eventType: "all" | BeatUnitEvents | BeatUnitEvents[]): ISubscription {
+ addSubscriber(subscriber: ISubscriber, eventType: BeatUnitEvent[]): { unbind: () => void } {
return this.publisher.addSubscriber(subscriber, eventType);
}
toggle(): void {
this.on = !this.on;
- this.publisher.notifySubs(BeatUnitEvents.Toggle);
+ this.publisher.notifySubs(BeatUnitEvent.Toggle);
if (this.on) {
- this.publisher.notifySubs(BeatUnitEvents.On);
+ this.publisher.notifySubs(BeatUnitEvent.On);
} else {
- this.publisher.notifySubs(BeatUnitEvents.Off);
+ this.publisher.notifySubs(BeatUnitEvent.Off);
}
}
setOn(on: boolean): void {
this.on = on;
- this.publisher.notifySubs(this.on ? BeatUnitEvents.On : BeatUnitEvents.Off);
+ this.publisher.notifySubs(this.on ? BeatUnitEvent.On : BeatUnitEvent.Off);
}
setType(type: BeatUnitType): void {
this.typeIndex = BeatUnit.TypeRotation.indexOf(type);
- this.publisher.notifySubs(BeatUnitEvents.TypeChange);
+ this.publisher.notifySubs(BeatUnitEvent.TypeChange);
}
getType(): BeatUnitType {
@@ -65,7 +64,7 @@ export default class BeatUnit implements IPublisher {
} else {
this.typeIndex += 1;
}
- this.publisher.notifySubs(BeatUnitEvents.TypeChange);
+ this.publisher.notifySubs(BeatUnitEvent.TypeChange);
}
isOn(): boolean {
diff --git a/src/Publisher.ts b/src/Publisher.ts
index 1c1d1a6..6a0c6cb 100644
--- a/src/Publisher.ts
+++ b/src/Publisher.ts
@@ -1,10 +1,10 @@
-import ISubscriber from "./Subscriber";
+import ISubscriber, {LEvent} from "./Subscriber";
-class Subscription implements ISubscription {
- private subscriber: ISubscriber;
- private readonly eventTypes: T[];
- private publisher: Publisher;
- constructor(publisher: Publisher, subscriber: ISubscriber, eventTypes: T[]) {
+class Subscription implements ISubscription {
+ private subscriber: ISubscriber;
+ private readonly eventTypes: EventType[];
+ private publisher: Publisher;
+ constructor(publisher: Publisher, subscriber: ISubscriber, eventTypes: EventType[]) {
this.subscriber = subscriber;
this.publisher = publisher;
this.eventTypes = eventTypes;
@@ -14,32 +14,36 @@ class Subscription implements ISubscription {
this.publisher.unbind(this);
}
- getEventTypes(): T[] {
+ getEventTypes(): EventType[] {
return this.eventTypes;
}
- getSubscriber(): ISubscriber {
+ getSubscriber(): ISubscriber {
return this.subscriber;
}
}
+interface EventSubscriberRecord {
+ get(key: K): ISubscriber[];
+ set(key: K, subscribers: ISubscriber[]): EventSubscriberRecord;
+}
-export class Publisher implements IPublisher {
- private subscribers: Map;
- private parent: P;
- constructor(parent: P) {
+export class Publisher implements IPublisher {
+ private subscribers: EventSubscriberRecord;
+ private parent: PublisherType;
+
+ constructor(parent: PublisherType) {
this.parent = parent;
this.subscribers = new Map();
- this.subscribers.set("all", []);
}
- addSubscriber(subscriber: ISubscriber, eventType: (T | "all") | T[]): ISubscription {
- let eventTypes: (T | "all")[] = [];
- if (!Array.isArray(eventType)) {
- eventTypes.push(eventType);
+ addSubscriber(subscriber: ISubscriber, subscribeTo: EventType | EventType[]): ISubscription {
+ let eventTypes: EventType[] = [];
+ if (!Array.isArray(subscribeTo)) {
+ eventTypes.push(subscribeTo);
} else {
- eventTypes = eventType as (T | "all")[];
+ eventTypes = subscribeTo;
}
for (const key of eventTypes) {
this.getSubscribers(key).push(subscriber);
@@ -47,17 +51,17 @@ export class Publisher implements IPublisher
return new Subscription(this, subscriber, eventTypes);
}
- unbind(subscription: Subscription): void {
+ unbind(subscription: Subscription): void {
for (const key of subscription.getEventTypes()) {
const subs = this.getSubscribers(key);
subs.splice(subs.indexOf(subscription.getSubscriber()), 1);
}
}
- private getSubscribers(key: T | "all"): ISubscriber[] {
+ private getSubscribers(key: K): ISubscriber[] {
const subscribersList = this.subscribers.get(key);
if (subscribersList === undefined) {
- const newList: ISubscriber[] = [];
+ const newList: ISubscriber[] = [];
this.subscribers.set(key, newList);
return newList;
} else {
@@ -65,18 +69,15 @@ export class Publisher implements IPublisher
}
}
- notifySubs(eventType: T): void {
+ notifySubs(eventType: K): void {
for (const sub of this.getSubscribers(eventType)) {
sub.notify(this.parent, eventType);
}
- for (const sub of this.getSubscribers("all")) {
- sub.notify(this.parent, eventType);
- }
}
}
-export interface IPublisher {
- addSubscriber(subscriber: ISubscriber, eventType: (T | "all") | T[]): {unbind: () => void};
+export interface IPublisher {
+ addSubscriber(subscriber: ISubscriber, subscribeTo: T | T[]): {unbind: () => void};
}
export interface ISubscription {
diff --git a/src/Subscriber.ts b/src/Subscriber.ts
index 2b24242..35afd2b 100644
--- a/src/Subscriber.ts
+++ b/src/Subscriber.ts
@@ -1,3 +1,4 @@
-export default interface ISubscriber {
- notify(publisher: unknown, event: T | "all" | T[]): void;
+export type LEvent = string;
+export default interface ISubscriber {
+ notify(publisher: unknown, event: T): void;
}
\ No newline at end of file
diff --git a/src/ui/Beat/BeatView.ts b/src/ui/Beat/BeatView.ts
index 6d9733d..88b3046 100644
--- a/src/ui/Beat/BeatView.ts
+++ b/src/ui/Beat/BeatView.ts
@@ -1,6 +1,5 @@
import UINode, {UINodeOptions} from "@/ui/UINode";
import Beat, {BeatEvents} from "@/Beat";
-import {IPublisher} from "@/Publisher";
import ISubscriber from "@/Subscriber";
import BeatUnitView from "@/ui/BeatUnit/BeatUnitView";
import "./Beat.css";
@@ -9,7 +8,17 @@ export type BeatUINodeOptions = UINodeOptions & {
beat: Beat,
};
-export default class BeatView extends UINode implements ISubscriber {
+const EventTypeSubscriptions = [
+ BeatEvents.NewName,
+ BeatEvents.NewTimeSig,
+ BeatEvents.NewBarCount,
+ BeatEvents.DisplayTypeChanged,
+ BeatEvents.LoopLengthChanged,
+];
+
+type EventTypeSubscriptions = FlatArray;
+
+export default class BeatView extends UINode implements ISubscriber {
private beat: Beat;
private title!: HTMLHeadingElement;
private beatUnitViews: BeatUnitView[] = [];
@@ -34,20 +43,20 @@ export default class BeatView extends UINode implements ISubscriber {
}
private setupBindings() {
- this.beat.addSubscriber(this, "all");
+ this.beat.addSubscriber(this, EventTypeSubscriptions);
}
- notify(publisher: IPublisher, event: "all" | T[] | T): void {
- if (event === BeatEvents.NewName) {
+ notify(publisher: unknown, event: EventTypeSubscriptions): void {
+ switch (event) {
+ case BeatEvents.NewName:
this.title.innerText = this.beat.getName();
- } else if (event === BeatEvents.NewTimeSig) {
- this.setupBeatUnits();
- } else if (event === BeatEvents.NewBarCount) {
- this.setupBeatUnits();
- } else if (event === BeatEvents.DisplayTypeChanged) {
- this.setupBeatUnits();
- } else if (event === BeatEvents.LoopLengthChanged) {
+ break;
+ case BeatEvents.NewTimeSig:
+ case BeatEvents.NewBarCount:
+ case BeatEvents.DisplayTypeChanged:
+ case BeatEvents.LoopLengthChanged:
this.setupBeatUnits();
+ break;
}
}
@@ -91,8 +100,9 @@ export default class BeatView extends UINode implements ISubscriber {
} else {
this.beatUnitViewBlock = UINode.make("div", {
classes: ["beat-unit-block"],
- subs: [...beatUnitNodes],
- });
+ }, [
+ ...beatUnitNodes
+ ]);
}
}
@@ -140,16 +150,14 @@ export default class BeatView extends UINode implements ISubscriber {
}
return UINode.make("div", {
classes: ["beat"],
- subs: [
- UINode.make("div", {
- classes: ["beat-main"],
- subs: [
- this.title,
- this.beatUnitViewBlock,
- ]
- }),
- ],
- });
+ }, [
+ UINode.make("div", {
+ classes: ["beat-main"],
+ }, [
+ this.title,
+ this.beatUnitViewBlock,
+ ]),
+ ]);
}
}
diff --git a/src/ui/BeatGroup/BeatGroupView.ts b/src/ui/BeatGroup/BeatGroupView.ts
index 83d9b8f..c987196 100644
--- a/src/ui/BeatGroup/BeatGroupView.ts
+++ b/src/ui/BeatGroup/BeatGroupView.ts
@@ -3,14 +3,18 @@ import BeatGroup, {BeatGroupEvents} from "@/BeatGroup";
import BeatView from "@/ui/Beat/BeatView";
import "./BeatGroup.css";
import ISubscriber from "@/Subscriber";
-import {IPublisher} from "@/Publisher";
export type BeatGroupUINodeOptions = UINodeOptions & {
title: string,
beatGroup: BeatGroup,
};
-export default class BeatGroupView extends UINode implements ISubscriber {
+const EventTypeSubscriptions = [
+ BeatGroupEvents.BeatListChanged
+];
+type EventTypeSubscriptions = FlatArray;
+
+export default class BeatGroupView extends UINode implements ISubscriber {
private title: string;
private beatGroup: BeatGroup;
private beatViews: BeatView[] = [];
@@ -22,7 +26,7 @@ export default class BeatGroupView extends UINode implements ISubscriber {
this.beatGroup.addSubscriber(this, BeatGroupEvents.BeatListChanged);
}
- notify(publisher: IPublisher, event: "all" | T[] | T): void {
+ notify(publisher: unknown, event: EventTypeSubscriptions): void {
if (event === BeatGroupEvents.BeatListChanged) {
this.redraw();
}
@@ -41,9 +45,8 @@ export default class BeatGroupView extends UINode implements ISubscriber {
}
return UINode.make("div", {
classes: ["beat-group"],
- subs: [
- ...this.beatViews.map(bv => bv.render())
- ],
- });
+ },[
+ ...this.beatViews.map(bv => bv.render())
+ ]);
}
}
\ No newline at end of file
diff --git a/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts b/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts
index f868d75..a4290bd 100644
--- a/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts
+++ b/src/ui/BeatGroupSettings/BeatGroupSettingsView.ts
@@ -1,9 +1,8 @@
import "./BeatGroupSettings.css";
import UINode, {UINodeOptions} from "@/ui/UINode";
import NumberInputView from "@/ui/Widgets/NumberInput/NumberInputView";
-import ISubscriber from "@/Subscriber";
+import ISubscriber, {SubscriptionEvent} from "@/Subscriber";
import BeatGroup, {BeatGroupEvents} from "@/BeatGroup";
-import {IPublisher} from "@/Publisher";
import {BeatEvents} from "@/Beat";
import BoolBoxView from "@/ui/Widgets/BoolBox/BoolBoxView";
import BeatSettingsView from "@/ui/BeatSettings/BeatSettingsView";
@@ -13,21 +12,22 @@ export type BeatGroupSettingsUINodeOptions = UINodeOptions & {
beatGroup: BeatGroup,
};
-export default class BeatGroupSettingsView extends UINode implements ISubscriber {
+const EventTypeSubscriptions = [
+ BeatGroupEvents.BarCountChanged,
+ BeatGroupEvents.TimeSigUpChanged,
+ BeatEvents.DisplayTypeChanged,
+ BeatGroupEvents.BeatListChanged,
+ BeatGroupEvents.LockingChanged,
+ BeatGroupEvents.AutoBeatSettingsChanged,
+];
+
+export default class BeatGroupSettingsView extends UINode implements ISubscriber {
private beatGroup: BeatGroup;
private barCountInput!: NumberInputView;
private timeSigUpInput!: NumberInputView;
private autoBeatLengthCheckbox!: BoolBoxView;
private beatSettingsViews: BeatSettingsView[] = [];
private beatSettingsContainer!: HTMLDivElement;
- private static readonly EventTypeSubscriptions = [
- BeatGroupEvents.BarCountChanged,
- BeatGroupEvents.TimeSigUpChanged,
- BeatEvents.DisplayTypeChanged,
- BeatGroupEvents.BeatListChanged,
- BeatGroupEvents.LockingChanged,
- BeatGroupEvents.AutoBeatSettingsChanged,
- ];
constructor(options: BeatGroupSettingsUINodeOptions) {
super(options);
@@ -38,14 +38,14 @@ export default class BeatGroupSettingsView extends UINode implements ISubscriber
setBeatGroup(newBeatGroup: BeatGroup): void {
this.beatGroup = newBeatGroup;
this.setupBindings();
- BeatGroupSettingsView.EventTypeSubscriptions.forEach(eventType => this.notify(null, eventType));
+ EventTypeSubscriptions.forEach(eventType => this.notify(null, eventType));
}
setupBindings(): void {
- this.beatGroup.addSubscriber(this, BeatGroupSettingsView.EventTypeSubscriptions);
+ this.beatGroup.addSubscriber(this, EventTypeSubscriptions);
}
- notify(publisher: IPublisher | null, event: "all" | T[] | T): void {
+ notify(publisher: unknown, event: SubscriptionEvent): void {
switch(event) {
case BeatGroupEvents.BarCountChanged:
this.barCountInput.setValue(this.beatGroup.getBarCount());
@@ -66,6 +66,8 @@ export default class BeatGroupSettingsView extends UINode implements ISubscriber
case BeatGroupEvents.AutoBeatSettingsChanged:
this.autoBeatLengthCheckbox.setValue(this.beatGroup.autoBeatLengthOn());
break;
+ case BeatEvents.DisplayTypeChanged:
+ break;
}
}
@@ -109,36 +111,32 @@ export default class BeatGroupSettingsView extends UINode implements ISubscriber
this.remakeBeatSettingsViews();
return UINode.make("div", {
classes: ["beat-group-settings"],
- subs: [
+ }, [
+ UINode.make("div", {
+ classes: ["beat-group-settings-options"],
+ }, [
UINode.make("div", {
- classes: ["beat-group-settings-options"],
- subs: [
- UINode.make("div", {
- classes: ["beat-group-settings-boxes", "beat-group-settings-option"],
- subs: [
- this.timeSigUpInput.render(),
- ],
- }),
- UINode.make("div", {
- classes: ["beat-group-settings-bar-count", "beat-group-settings-option"],
- subs: [
- this.barCountInput.render(),
- ],
- }),
- UINode.make("div", {
- classes: ["beat-group-settings-bar-count", "beat-group-settings-option"],
- subs: [
- this.autoBeatLengthCheckbox.render(),
- ],
- }),
- new ActionButtonView({
- label: "New Track",
- onClick: () => this.beatGroup.addBeat(),
- }).render(),
- this.beatSettingsContainer,
- ],
- }),
- ],
- });
+ classes: ["beat-group-settings-boxes", "beat-group-settings-option"],
+ }, [
+ this.timeSigUpInput.render(),
+ ]),
+ UINode.make("div", {
+ classes: ["beat-group-settings-bar-count", "beat-group-settings-option"]
+ ,
+ }, [
+ this.barCountInput.render(),
+ ]),
+ UINode.make("div", {
+ classes: ["beat-group-settings-bar-count", "beat-group-settings-option"],
+ }, [
+ this.autoBeatLengthCheckbox.render(),
+ ]),
+ new ActionButtonView({
+ label: "New Track",
+ onClick: () => this.beatGroup.addBeat(),
+ }).render(),
+ this.beatSettingsContainer,
+ ]),
+ ]);
}
}
\ No newline at end of file
diff --git a/src/ui/BeatSettings/BeatSettingsView.ts b/src/ui/BeatSettings/BeatSettingsView.ts
index b3076ae..c32e2a6 100644
--- a/src/ui/BeatSettings/BeatSettingsView.ts
+++ b/src/ui/BeatSettings/BeatSettingsView.ts
@@ -1,8 +1,8 @@
import "./BeatSettings.css";
import Beat, {BeatEvents} from "@/Beat";
import UINode, {UINodeOptions} from "@/ui/UINode";
-import ISubscriber from "@/Subscriber";
-import {IPublisher, ISubscription} from "@/Publisher";
+import ISubscriber, {SubscriptionEvent} from "@/Subscriber";
+import {ISubscription} from "@/Publisher";
import NumberInputView from "@/ui/Widgets/NumberInput/NumberInputView";
import BoolBoxView from "@/ui/Widgets/BoolBox/BoolBoxView";
import ActionButtonView from "@/ui/Widgets/ActionButton/ActionButtonView";
@@ -11,7 +11,13 @@ export type BeatSettingsViewUINodeOptions = UINodeOptions & {
beat: Beat,
};
-export default class BeatSettingsView extends UINode implements ISubscriber {
+const EventTypeSubscriptions = [
+ BeatEvents.NewName,
+ BeatEvents.LoopLengthChanged,
+ BeatEvents.DisplayTypeChanged,
+];
+
+export default class BeatSettingsView extends UINode implements ISubscriber {
private beat: Beat;
private loopLengthInput!: NumberInputView;
private bakeButton!: ActionButtonView;
@@ -37,18 +43,19 @@ export default class BeatSettingsView extends UINode implements ISubscriber {
this.sub.unbind();
this.beat = beat;
this.setupBindings();
- this.notify(null, BeatEvents.NewName);
- this.notify(null, BeatEvents.LoopLengthChanged);
- this.notify(null, BeatEvents.DisplayTypeChanged);
+ EventTypeSubscriptions.forEach(eventType => this.notify(null, eventType));
}
- notify(publisher: IPublisher | null, event: "all" | T[] | T): void {
- if (event === BeatEvents.NewName) {
+ notify(publisher: unknown, event: SubscriptionEvent): void {
+ switch(event) {
+ case BeatEvents.NewName:
this.titleInput.value = this.beat.getName();
this.titleDisplay.innerText = this.beat.getName();
- } else if (event === BeatEvents.LoopLengthChanged) {
+ break;
+ case BeatEvents.LoopLengthChanged:
this.loopLengthInput.setValue(this.beat.getLoopLength());
- } else if (event === BeatEvents.DisplayTypeChanged) {
+ break;
+ case BeatEvents.DisplayTypeChanged:
this.loopCheckbox.setValue(this.beat.isLooping());
this.bakeButton.setDisabled(!this.beat.isLooping());
if (this.beat.isLooping()) {
@@ -56,6 +63,7 @@ export default class BeatSettingsView extends UINode implements ISubscriber {
} else {
this.loopLengthSection.classList.add("hide");
}
+ break;
}
}
@@ -102,10 +110,9 @@ export default class BeatSettingsView extends UINode implements ISubscriber {
});
this.loopLengthSection = UINode.make("div", {
classes: ["loop-settings-option"],
- subs: [
- this.loopLengthInput.render(),
- ],
- });
+ }, [
+ this.loopLengthInput.render(),
+ ]);
if (this.beat.isLooping()) {
this.loopLengthSection.classList.remove("hide");
} else {
@@ -113,28 +120,25 @@ export default class BeatSettingsView extends UINode implements ISubscriber {
}
return UINode.make("div", {
classes: ["beat-settings"],
- subs: [
- this.titleDisplay,
+ }, [
+ this.titleDisplay,
+ UINode.make("div", {
+ classes: ["beat-settings-lower"],
+ }, [
+ this.bakeButton.render(),
+ new ActionButtonView({
+ icon: "trash",
+ type: "secondary",
+ alt: "Delete Track",
+ onClick: () => this.beat.delete(),
+ }).render(),
UINode.make("div", {
- classes: ["beat-settings-lower"],
- subs: [
- this.bakeButton.render(),
- new ActionButtonView({
- icon: "trash",
- type: "secondary",
- alt: "Delete Track",
- onClick: () => this.beat.delete(),
- }).render(),
- UINode.make("div", {
- classes: ["loop-settings"],
- subs: [
- this.loopCheckbox.render(),
- ]
- }),
- this.loopLengthSection,
- ]
- }),
- ],
- });
+ classes: ["loop-settings"],
+ }, [
+ this.loopCheckbox.render(),
+ ]),
+ this.loopLengthSection,
+ ]),
+ ]);
}
}
diff --git a/src/ui/BeatUnit/BeatUnitView.ts b/src/ui/BeatUnit/BeatUnitView.ts
index c4d452f..8107705 100644
--- a/src/ui/BeatUnit/BeatUnitView.ts
+++ b/src/ui/BeatUnit/BeatUnitView.ts
@@ -1,4 +1,4 @@
-import BeatUnit, {BeatUnitEvents, BeatUnitType} from "@/BeatUnit";
+import BeatUnit, {BeatUnitEvent, BeatUnitType} from "@/BeatUnit";
import ISubscriber from "@/Subscriber";
import UINode, {UINodeOptions} from "@/ui/UINode";
import {IPublisher, ISubscription, Publisher} from "@/Publisher";
@@ -8,10 +8,17 @@ export type BeatUnitUINodeOptions = UINodeOptions & {
beatUnit: BeatUnit,
};
-export default class BeatUnitView extends UINode implements ISubscriber {
+const EventTypeSubscriptions = [
+ BeatUnitEvent.On,
+ BeatUnitEvent.Off,
+ BeatUnitEvent.TypeChange,
+];
+type EventTypeSubscriptions = FlatArray;
+
+export default class BeatUnitView extends UINode implements ISubscriber {
private beatUnit: BeatUnit;
private subscription: ISubscription | null = null;
- private publisher: IPublisher = new Publisher(this);
+ private publisher: IPublisher = new Publisher(this);
private touchTimeout: ReturnType | null = null;
@@ -24,13 +31,13 @@ export default class BeatUnitView extends UINode implements ISubscriber {
setUnit(beatUnit: BeatUnit): void {
this.beatUnit = beatUnit;
this.setupBindings();
- this.notify(this.publisher, beatUnit.isOn() ? BeatUnitEvents.On : BeatUnitEvents.Off);
- this.notify(this.publisher, BeatUnitEvents.TypeChange);
+ this.notify(this.publisher, beatUnit.isOn() ? BeatUnitEvent.On : BeatUnitEvent.Off);
+ this.notify(this.publisher, BeatUnitEvent.TypeChange);
}
private setupBindings() {
this.subscription?.unbind();
- this.subscription = this.beatUnit.addSubscriber(this, "all");
+ this.subscription = this.beatUnit.addSubscriber(this, EventTypeSubscriptions);
this.onMouseDown((ev: MouseEvent) => {
if (ev.button === 1) {
this.beatUnit.rotateType();
@@ -40,7 +47,7 @@ export default class BeatUnitView extends UINode implements ISubscriber {
this.touchTimeout = setTimeout(() => {
this.beatUnit.rotateType();
this.touchTimeout = null;
- }, 600);
+ }, 400);
});
this.getNode().addEventListener("touchend", () => {
if (this.touchTimeout) {
@@ -62,12 +69,15 @@ export default class BeatUnitView extends UINode implements ISubscriber {
this.beatUnit.setOn(false);
}
- notify(publisher: IPublisher, event: "all" | T[] | T): void {
- if (event === BeatUnitEvents.On) {
+ notify(publisher: unknown, event: EventTypeSubscriptions): void {
+ switch (event) {
+ case BeatUnitEvent.On:
this.getNode().classList.add("beat-unit-on");
- } else if (event === BeatUnitEvents.Off) {
+ break;
+ case BeatUnitEvent.Off:
this.getNode().classList.remove("beat-unit-on");
- } else if (event === BeatUnitEvents.TypeChange) {
+ break;
+ case BeatUnitEvent.TypeChange:
switch (this.beatUnit.getType()) {
case BeatUnitType.Normal:
this.getNode().classList.remove("beat-unit-ghost");
@@ -82,6 +92,7 @@ export default class BeatUnitView extends UINode implements ISubscriber {
this.getNode().classList.add("beat-unit-accent");
break;
}
+ break;
}
}
diff --git a/src/ui/Root/RootView.ts b/src/ui/Root/RootView.ts
index ce4e038..a65b7c5 100644
--- a/src/ui/Root/RootView.ts
+++ b/src/ui/Root/RootView.ts
@@ -15,13 +15,13 @@ export default class RootView extends UINode {
private beatGroupView: BeatGroupView;
private mainBeatGroup: BeatGroup;
private beatGroupSettingsView!: BeatGroupSettingsView;
- private sidebar!: HTMLDivElement;
constructor(options: RootUINodeOptions) {
super(options);
this.mainBeatGroup = options.mainBeatGroup ?? RootView.defaultMainBeatGroup();
this.beatGroupView = new BeatGroupView({title: options.title, beatGroup: this.mainBeatGroup});
+ this.beatGroupSettingsView = new BeatGroupSettingsView({beatGroup: this.mainBeatGroup});
this.title = options.title;
}
@@ -47,68 +47,77 @@ export default class RootView extends UINode {
this.getNode().classList.toggle("vertical-mode");
}
- build(): HTMLElement {
- this.beatGroupSettingsView = new BeatGroupSettingsView({beatGroup: this.mainBeatGroup});
- const sidebarMain = UINode.make("div", {
- classes: ["root-settings"],
- subs: [
- UINode.make("h1", {innerText: this.title, classes: ["root-title"]}),
- this.beatGroupSettingsView.render(),
- ]
- });
- const sidebarStrip = UINode.make("div", {
- classes: ["root-sidebar-toggle"],
- subs: [
- UINode.make("div", {
- classes: ["root-quick-access-button"],
- subs: [new IconView({iconName: "list", color: "var(--color-ui-neutral-dark)"}).render()],
- onclick: () => this.toggleSidebar(),
- }),
- UINode.make("div", {
- classes: ["root-quick-access-button"],
- subs: [new IconView({iconName: "arrowClockwise", color: "var(--color-ui-neutral-dark)"}).render()],
- onclick: () => this.toggleOrientation(),
- }),
- UINode.make("div", {
- classes: ["root-quick-access-button"],
- subs: [new IconView({iconName: "snowflake", color: "var(--color-ui-neutral-dark)"}).render()],
- onclick: () => this.mainBeatGroup.bakeLoops(),
- }),
- UINode.make("div", {
- classes: ["root-quick-access-button"],
- title: "Reset all",
- subs: [new IconView({iconName: "trash", color: "var(--color-ui-neutral-dark)"}).render()],
- onclick: () => {
- this.mainBeatGroup = RootView.defaultMainBeatGroup();
- this.beatGroupSettingsView.setBeatGroup(this.mainBeatGroup);
- this.beatGroupView.setBeatGroup(this.mainBeatGroup);
- },
- }),
- ]
- });
- this.sidebar = UINode.make("div", {
- classes: ["root-sidebar"],
- subs: [
- sidebarMain,
- sidebarStrip,
- ]
- });
+ private buildSidebarStrip(): HTMLElement {
return UINode.make("div", {
- classes: ["root", "sidebar-visible"],
- subs: [
- this.sidebar,
- UINode.make("div", {
- classes: ["root-beat-stage-container"],
- subs: [
- UINode.make("div", {
- classes: ["root-beat-stage"],
- subs: [
- this.beatGroupView.render(),
- ],
- })
- ]
- })
- ],
- });
+ classes: ["root-sidebar-toggle"],
+ }, [
+ UINode.make("div", {
+ classes: ["root-quick-access-button"],
+ onclick: () => this.toggleSidebar(),
+ }, [
+ new IconView({
+ iconName: "list",
+ color: "var(--color-ui-neutral-dark)"
+ }).render()
+ ]),
+ UINode.make("div", {
+ classes: ["root-quick-access-button"],
+ onclick: () => this.toggleOrientation(),
+ }, [
+ new IconView({
+ iconName: "arrowClockwise",
+ color: "var(--color-ui-neutral-dark)"
+ }).render(),
+ ]),
+ UINode.make("div", {
+ classes: ["root-quick-access-button"],
+ onclick: () => this.mainBeatGroup.bakeLoops(),
+ }, [
+ new IconView({
+ iconName: "snowflake",
+ color: "var(--color-ui-neutral-dark)"
+ }).render(),
+ ]),
+ UINode.make("div", {
+ classes: ["root-quick-access-button"],
+ title: "Reset all",
+ onclick: () => {
+ this.mainBeatGroup = RootView.defaultMainBeatGroup();
+ this.beatGroupSettingsView.setBeatGroup(this.mainBeatGroup);
+ this.beatGroupView.setBeatGroup(this.mainBeatGroup);
+ },
+ }, [
+ new IconView({
+ iconName: "trash",
+ color: "var(--color-ui-neutral-dark)"
+ }).render()
+ ]),
+ ]);
+ }
+
+ private buildSidebar(): HTMLElement {
+ return (
+ UINode.make("div", {classes: ["root-sidebar"]}, [
+ UINode.make("div", {classes: ["root-settings"]}, [
+ UINode.make("h1", {classes: ["root-title"], innerText: this.title}, [
+ this.beatGroupSettingsView.render(),
+ ]),
+ this.buildSidebarStrip(),
+ ]),
+ ])
+ );
+ }
+
+ build(): HTMLElement {
+ return (
+ UINode.make("div", {classes: ["root", "sidebar-visible"]}, [
+ this.buildSidebar(),
+ UINode.make("div", {classes: ["root-beat-stage-container"]}, [
+ UINode.make("div", {classes: ["root-beat-stage"]}, [
+ this.beatGroupView.render(),
+ ])
+ ])
+ ])
+ );
}
}
\ No newline at end of file
diff --git a/src/ui/UINode.ts b/src/ui/UINode.ts
index d8a4993..daaa393 100644
--- a/src/ui/UINode.ts
+++ b/src/ui/UINode.ts
@@ -50,20 +50,22 @@ export default abstract class UINode {
T extends keyof HTMLElementTagNameMap,
K extends keyof HTMLElementTagNameMap[T]>(
type: T,
- attributes: IRenderAttributes
+ attributes: IRenderAttributes,
+ subElements?: HTMLElement[],
): 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 === "subs") {
- element.append(...attributes.subs!);
} else {
element[key as keyof HTMLElementTagNameMap[T]] = (attributes as any)[key];
}
}
}
+ if (subElements) {
+ element.append(...subElements);
+ }
return element;
}
diff --git a/webpack.config.js b/webpack.config.js
index 5d8c089..e40e320 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -50,14 +50,14 @@ const webpackConfig = {
output: {
filename: "bundle.js",
- publicPath: "./static/",
+ publicPath: `${config.baseUrl}/static/`,
path: path.resolve(__dirname, "./public/static"),
},
devServer: {
static: {
directory: path.join(__dirname, "./public"),
- publicPath: "/",
+ publicPath: `${config.baseUrl}/`,
},
hot: true,
compress: true,