fixed button styling, dev server, added switches for event handling, global baking, global reset, three BeatUnit states with styles, and mobile long touch to change beatunit type

This commit is contained in:
Daniel Ledda
2022-03-20 21:21:43 +01:00
parent 3ba01eb86b
commit 9121706184
15 changed files with 222 additions and 113 deletions

View File

@@ -2,7 +2,7 @@ import UINode, {UINodeOptions} from "@/ui/UINode";
import Beat, {BeatEvents} from "@/Beat";
import {IPublisher} from "@/Publisher";
import ISubscriber from "@/Subscriber";
import BeatUnitView from "./BeatUnit/BeatUnitView";
import BeatUnitView from "@/ui/BeatUnit/BeatUnitView";
import "./Beat.css";
export type BeatUINodeOptions = UINodeOptions & {

View File

@@ -1,37 +0,0 @@
.beat-unit {
width: 2em;
height: 2em;
margin-right: 4px;
background-color: var(--color-ui-neutral-dark);
border-width: 0.1em 0.1em 0.1em 0.1em;
border-color: var(--color-ui-neutral-dark);
border-style: solid;
display: inline-block;
transition: background-color 150ms;
cursor: pointer;
}
.vertical-mode .beat-unit {
margin-bottom: 4px;
display: block;
}
.beat-unit:hover {
border-color: var(--color-ui-neutral-dark-hover);
background-color: var(--color-ui-neutral-dark-hover);
transition: none;
}
.beat-unit.beat-unit-ghost:hover {
background-color: #c9e2c9;
}
.beat-unit.beat-unit-on {
background-color: var(--color-ui-accent);
border-color: var(--color-ui-accent);
transition: none;
}
.beat-unit.beat-unit-on.beat-unit-ghost {
background-color: darkseagreen;
}

View File

@@ -1,6 +1,6 @@
import UINode, {UINodeOptions} from "@/ui/UINode";
import BeatGroup, {BeatGroupEvents} from "@/BeatGroup";
import BeatView from "./Beat/BeatView";
import BeatView from "@/ui/Beat/BeatView";
import "./BeatGroup.css";
import ISubscriber from "@/Subscriber";
import {IPublisher} from "@/Publisher";
@@ -28,6 +28,12 @@ export default class BeatGroupView extends UINode implements ISubscriber {
}
}
setBeatGroup(newBeatGroup: BeatGroup): void {
this.beatGroup = newBeatGroup;
this.beatGroup.addSubscriber(this, BeatGroupEvents.BeatListChanged);
this.redraw();
}
build(): HTMLDivElement {
this.beatViews = [];
for (let i = 0; i < this.beatGroup.getBeatCount(); i++) {

View File

@@ -20,35 +20,52 @@ export default class BeatGroupSettingsView extends UINode implements ISubscriber
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);
this.beatGroup = options.beatGroup;
this.beatGroup.addSubscriber(this, [
BeatGroupEvents.BarCountChanged,
BeatGroupEvents.TimeSigUpChanged,
BeatEvents.DisplayTypeChanged,
BeatGroupEvents.BeatListChanged,
BeatGroupEvents.LockingChanged,
BeatGroupEvents.AutoBeatSettingsChanged,
]);
this.setupBindings();
}
notify<T extends string | number>(publisher: IPublisher<T>, event: "all" | T[] | T): void {
if (event === BeatGroupEvents.BarCountChanged) {
setBeatGroup(newBeatGroup: BeatGroup): void {
this.beatGroup = newBeatGroup;
this.setupBindings();
BeatGroupSettingsView.EventTypeSubscriptions.forEach(eventType => this.notify(null, eventType));
}
setupBindings(): void {
this.beatGroup.addSubscriber(this, BeatGroupSettingsView.EventTypeSubscriptions);
}
notify<T extends string | number>(publisher: IPublisher<T> | null, event: "all" | T[] | T): void {
switch(event) {
case BeatGroupEvents.BarCountChanged:
this.barCountInput.setValue(this.beatGroup.getBarCount());
} else if (event === BeatGroupEvents.TimeSigUpChanged) {
break;
case BeatGroupEvents.TimeSigUpChanged:
this.timeSigUpInput.setValue(this.beatGroup.getTimeSigUp());
} else if (event === BeatGroupEvents.BeatListChanged) {
break;
case BeatGroupEvents.BeatListChanged:
this.remakeBeatSettingsViews();
} else if (event === BeatGroupEvents.LockingChanged) {
break;
case BeatGroupEvents.LockingChanged:
if (this.beatGroup.barsLocked()) {
this.barCountInput.disable();
} else {
this.barCountInput.enable();
}
} else if (event === BeatGroupEvents.AutoBeatSettingsChanged) {
break;
case BeatGroupEvents.AutoBeatSettingsChanged:
this.autoBeatLengthCheckbox.setValue(this.beatGroup.autoBeatLengthOn());
break;
}
}

View File

@@ -0,0 +1,55 @@
.beat-unit {
width: 2em;
height: 2em;
margin-right: 4px;
background-color: #464646;
border-color: #464646;
border-width: 0.1em 0.1em 0.1em 0.1em;
border-style: solid;
display: inline-block;
transition: background-color 100ms, border-color 100ms;
cursor: pointer;
}
.beat-unit:hover {
border-color: #5f5f5f;
background-color: #5f5f5f;
transition: none;
}
.vertical-mode .beat-unit {
margin-bottom: 4px;
display: block;
}
.beat-unit.beat-unit-on {
border-color: var(--color-ui-accent);
background-color: var(--color-ui-accent);
transition: none;
}
.beat-unit.beat-unit-on:hover {
border-color: var(--color-ui-accent-hover);
background-color: var(--color-ui-accent-hover);
}
.beat-unit.beat-unit-on.beat-unit-ghost {
border-color: var(--color-ui-neutral-light);
background-color: var(--color-ui-accent);
}
.beat-unit.beat-unit-on.beat-unit-ghost:hover {
border-color: var(--color-ui-neutral-light);
background-color: var(--color-ui-accent-hover);
}
.beat-unit.beat-unit-on.beat-unit-accent {
border-color: var(--color-ui-accent);
background-color: var(--color-ui-accent);
opacity: 60%;
}
.beat-unit.beat-unit-on.beat-unit-accent:hover {
border-color: var(--color-ui-accent-hover);
background-color: var(--color-ui-accent-hover);
}

View File

@@ -12,6 +12,8 @@ export default class BeatUnitView extends UINode implements ISubscriber {
private beatUnit: BeatUnit;
private subscription: ISubscription | null = null;
private publisher: IPublisher<BeatUnitEvents> = new Publisher<BeatUnitEvents, BeatUnitView>(this);
private touchTimeout: ReturnType<typeof setTimeout> | null = null;
constructor(options: BeatUnitUINodeOptions) {
super(options);
@@ -29,10 +31,21 @@ export default class BeatUnitView extends UINode implements ISubscriber {
private setupBindings() {
this.subscription?.unbind();
this.subscription = this.beatUnit.addSubscriber(this, "all");
this.onMouseUp((ev: MouseEvent) => {
this.onMouseDown((ev: MouseEvent) => {
if (ev.button === 1) {
const currentType = this.beatUnit.getType();
this.beatUnit.setType(currentType === BeatUnitType.GhostNote ? BeatUnitType.Normal : BeatUnitType.GhostNote);
this.beatUnit.rotateType();
}
});
this.getNode().addEventListener("touchstart", () => {
this.touchTimeout = setTimeout(() => {
this.beatUnit.rotateType();
this.touchTimeout = null;
}, 600);
});
this.getNode().addEventListener("touchend", () => {
if (this.touchTimeout) {
clearTimeout(this.touchTimeout);
this.touchTimeout = null;
}
});
}
@@ -55,12 +68,19 @@ export default class BeatUnitView extends UINode implements ISubscriber {
} else if (event === BeatUnitEvents.Off) {
this.getNode().classList.remove("beat-unit-on");
} else if (event === BeatUnitEvents.TypeChange) {
const showingAsGhost = this.getNode().classList.contains("beat-unit-ghost");
const isGhost = this.beatUnit.getType() === BeatUnitType.GhostNote;
if (isGhost && !showingAsGhost) {
this.getNode().classList.add("beat-unit-ghost");
} else if (!isGhost && showingAsGhost) {
switch (this.beatUnit.getType()) {
case BeatUnitType.Normal:
this.getNode().classList.remove("beat-unit-ghost");
this.getNode().classList.remove("beat-unit-accent");
break;
case BeatUnitType.GhostNote:
this.getNode().classList.remove("beat-unit-accent");
this.getNode().classList.add("beat-unit-ghost");
break;
case BeatUnitType.Accent:
this.getNode().classList.remove("beat-unit-ghost");
this.getNode().classList.add("beat-unit-accent");
break;
}
}
}
@@ -77,14 +97,14 @@ export default class BeatUnitView extends UINode implements ISubscriber {
}
onHover(cb: () => void): void {
this.getNode().onmouseover = cb;
this.getNode().addEventListener("mouseover", cb);
}
onMouseDown(cb: (ev: MouseEvent) => void): void {
this.getNode().onmousedown = cb;
this.getNode().addEventListener("mousedown", cb);
}
onMouseUp(cb: (ev: MouseEvent) => void): void {
this.getNode().onmouseup = cb;
this.getNode().addEventListener("mouseup", cb);
}
}

View File

@@ -64,11 +64,12 @@
left: 0;
}
.root-hamburger, .root-switch-mode {
.root-quick-access-button {
right: 0;
width: 2em;
height: 2em;
cursor: pointer;
margin-bottom: 0.5em;
}
.root-beat-stage-container {

View File

@@ -7,7 +7,7 @@ import IconView from "@/ui/Widgets/Icon/IconView";
export type RootUINodeOptions = UINodeOptions & {
title: string,
mainBeatGroup: BeatGroup,
mainBeatGroup?: BeatGroup,
};
export default class RootView extends UINode {
@@ -20,11 +20,25 @@ export default class RootView extends UINode {
constructor(options: RootUINodeOptions) {
super(options);
this.mainBeatGroup = options.mainBeatGroup;
this.mainBeatGroup = options.mainBeatGroup ?? RootView.defaultMainBeatGroup();
this.beatGroupView = new BeatGroupView({title: options.title, beatGroup: this.mainBeatGroup});
this.title = options.title;
}
static defaultMainBeatGroup(): BeatGroup {
const defaultSettings = {
barCount: 2,
isLooping: false,
timeSigUp: 8,
};
const mainBeatGroup = new BeatGroup(defaultSettings);
mainBeatGroup.addBeat({name: "LF"});
mainBeatGroup.addBeat({name: "LH"});
mainBeatGroup.addBeat({name: "RH"});
mainBeatGroup.addBeat({name: "RF"});
return mainBeatGroup;
}
toggleSidebar(): void {
this.getNode().classList.toggle("sidebar-visible");
}
@@ -46,15 +60,30 @@ export default class RootView extends UINode {
classes: ["root-sidebar-toggle"],
subs: [
UINode.make("div", {
classes: ["root-hamburger"],
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-switch-mode"],
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", {

View File

@@ -14,11 +14,11 @@
background-color: var(--color-ui-accent);
color: var(--color-p-light);
}
.action-button.action-button-primary:hover:not.disabled {
.action-button.action-button-primary:hover {
background-color: var(--color-ui-accent-hover);
color: var(--color-p-light-hover);
}
.action-button.action-button-primary:active:not.disabled {
.action-button.action-button-primary:active {
background-color: var(--color-ui-accent-active);
color: var(--color-p-light-active);
}
@@ -27,11 +27,11 @@
background-color: var(--color-ui-neutral-dark);
color: var(--color-p-light);
}
.action-button.action-button-secondary:hover:not.disabled {
.action-button.action-button-secondary:hover:not(.disabled) {
background-color: var(--color-ui-neutral-dark-hover);
color: var(--color-p-light-hover);
}
.action-button.action-button-secondary:active:not.disabled {
.action-button.action-button-secondary:active:not(.disabled) {
background-color: var(--color-ui-neutral-dark-active);
color: var(--color-p-light-active);
}