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