diff --git a/src/TrackUnit.ts b/src/TrackUnit.ts index 2f755ca..0d7e716 100644 --- a/src/TrackUnit.ts +++ b/src/TrackUnit.ts @@ -8,6 +8,13 @@ export const enum TrackUnitType { GhostNoteAccent="tut-3", } +export type TrackUnitStickingType = + | "none" + | "lh" + | "rh" + | "lf" + | "rf"; + export const enum TrackUnitEvent { Toggle="tue-0", On="tue-1", @@ -15,7 +22,6 @@ export const enum TrackUnitEvent { TypeChange="tue-3", } - export default class TrackUnit implements IPublisher { private static readonly TypeRotation = [ TrackUnitType.Normal, @@ -26,6 +32,7 @@ export default class TrackUnit implements IPublisher { private publisher: Publisher = new Publisher(this); private on = false; private typeIndex = 0; + private stickingType: TrackUnitStickingType = "none"; private parent: Track; constructor(options: { @@ -65,6 +72,17 @@ export default class TrackUnit implements IPublisher { this.parent.alertDeepChange(); } + setStickingType(type: TrackUnitStickingType) { + console.log('set stickin'); + this.stickingType = type; + this.publisher.notifySubs(TrackUnitEvent.TypeChange); + this.parent.alertDeepChange(); + } + + getStickingType() { + return this.stickingType; + } + getType(): TrackUnitType { return TrackUnit.TypeRotation[this.typeIndex]; } @@ -86,5 +104,6 @@ export default class TrackUnit implements IPublisher { mimic(trackUnit: TrackUnit): void { this.setOn(trackUnit.isOn()); this.setType(trackUnit.getType()); + this.setStickingType(trackUnit.getStickingType()); } } diff --git a/src/ui/Beat/BeatView.tsx b/src/ui/Beat/BeatView.tsx index 8063407..74ec447 100644 --- a/src/ui/Beat/BeatView.tsx +++ b/src/ui/Beat/BeatView.tsx @@ -3,8 +3,10 @@ import Beat, { BeatEvents } from "@/Beat"; import TrackView from "@/ui/Track/TrackView"; import "./Beat.css"; import EditableTextFieldView from "@/ui/Widgets/EditableTextFIeld/EditableTextFieldView"; +import AppState from "@/AppState"; export type BeatUINodeOptions = RungOptions & { + state: AppState, beat: Beat, orientation?: "horizontal" | "vertical", }; @@ -21,9 +23,11 @@ export default class BeatView extends Rung implements ISubscriber implements ISubscriber implements ISubscriber as HTMLDivElement; } -} \ No newline at end of file +} diff --git a/src/ui/Root/Root.css b/src/ui/Root/Root.css index d4b8f05..bea8250 100644 --- a/src/ui/Root/Root.css +++ b/src/ui/Root/Root.css @@ -138,6 +138,23 @@ transition: background-color 200ms; } +.root-toolbox { + display: flex; +} + +.root-toolbox .toolbox-button { + padding: 1em; + cursor: pointer; + color: black; + background-color: var(--color-ui-neutral-dark); +} +.root-toolbox .toolbox-button:hover { + background-color: var(--color-ui-neutral-dark-hover); +} +.root-toolbox .toolbox-button.active { + background-color: var(--color-ui-neutral-dark-active); +} + @media screen and (max-width: 900px) { .sidebar-visible .root-sidebar { left: 0; diff --git a/src/ui/Root/RootView.tsx b/src/ui/Root/RootView.tsx index ba5f8cc..712fcf5 100644 --- a/src/ui/Root/RootView.tsx +++ b/src/ui/Root/RootView.tsx @@ -4,7 +4,9 @@ import "./Root.css"; import BeatSettingsView from "@/ui/BeatSettings/BeatSettingsView"; import IconView from "@/ui/Widgets/Icon/IconView"; import BeatStore from "@/BeatStore"; -import { Capsule, h, frag, Rung, RungOptions, ICapsule } from "@djledda/ladder"; +import { Capsule, h, frag, Rung, RungOptions, ICapsule, ISubscriber } from "@djledda/ladder"; +import AppState from "@/AppState"; +import ToolboxView from "./ToolboxView"; export type RootUINodeOptions = RungOptions & { title: string, @@ -22,6 +24,8 @@ export default class RootView extends Rung { private showHideSidebarButton = Capsule.new(null); private sidebarActive = true; private sidebarLeftTabs = Capsule.new(null); + private state = new AppState(); + private toolboxView: ToolboxView; constructor(options: RootUINodeOptions) { super(options); @@ -29,6 +33,7 @@ export default class RootView extends Rung { loadFromLocalStorage: true, autoSave: true, }); + this.toolboxView = new ToolboxView({ state: this.state }); this.activeBeat = this.beatStore.getActiveBeat(); this.activeBeat.watch((newVal) => { this.beatSettingsView.setBeat(newVal); @@ -36,6 +41,7 @@ export default class RootView extends Rung { }); this.currentOrientation = this.beatStore.getSavedOrientation() ?? options.orientation ?? "horizontal"; this.beatView = new BeatView({ + state: this.state, beat: this.activeBeat.val, orientation: this.currentOrientation, }); @@ -169,12 +175,14 @@ export default class RootView extends Rung { as HTMLElement; }; + build() { return (
+ {this.toolboxView} {this.beatView}
diff --git a/src/ui/Track/TrackView.tsx b/src/ui/Track/TrackView.tsx index b478912..03f6506 100644 --- a/src/ui/Track/TrackView.tsx +++ b/src/ui/Track/TrackView.tsx @@ -2,8 +2,10 @@ import Track, { TrackEvents } from "@/Track"; import TrackUnitView from "@/ui/TrackUnit/TrackUnitView"; import "./Track.css"; import { Capsule, h, ISubscriber, ISubscription, Rung, RungOptions } from "@djledda/ladder"; +import AppState from "@/AppState"; export type TrackUINodeOptions = RungOptions & { + state: AppState, track: Track, }; @@ -24,11 +26,13 @@ export default class TrackView extends Rung implements ISubscriber as HTMLHeadingElement; this.setTrack(options.track); } @@ -89,16 +93,24 @@ export default class TrackView extends Rung implements ISubscriber implements ISubscri private handleMouseDown(ev: MouseEvent): void { if (ev.button === 1) { this.trackUnit.rotateType(); - } else if (ev.button === 0) { - this.toggle(); + } else { this.rotationTimeout = this.rotationTimeout || setTimeout(() => { this.trackUnit.rotateType(); this.blockNextMouseUp = true; @@ -103,6 +102,14 @@ export default class TrackUnitView extends Rung implements ISubscri this.trackUnit.toggle(); } + setStickingType(type: TrackUnitStickingType): void { + this.trackUnit.setStickingType(type); + } + + setType(type: TrackUnitType): void { + this.trackUnit.setType(type); + } + turnOn(): void { this.trackUnit.setOn(true); } @@ -120,24 +127,39 @@ export default class TrackUnitView extends Rung implements ISubscri this.render().classList.remove("track-unit-on"); break; case TrackUnitEvent.TypeChange: - switch (this.trackUnit.getType()) { - case TrackUnitType.Normal: - this.render().classList.remove("track-unit-ghost"); - this.render().classList.remove("track-unit-accent"); - break; - case TrackUnitType.GhostNote: - this.render().classList.add("track-unit-ghost"); - this.render().classList.remove("track-unit-accent"); - break; - case TrackUnitType.Accent: - this.render().classList.remove("track-unit-ghost"); - this.render().classList.add("track-unit-accent"); - break; - case TrackUnitType.GhostNoteAccent: - this.render().classList.add("track-unit-ghost"); - this.render().classList.add("track-unit-accent"); - break; - } + this.syncTrackUnitType(); + this.syncStickingType(this.trackUnit.getStickingType()); + } + } + + private syncStickingType(type: TrackUnitStickingType) { + const node = this.render(); + node.classList.remove("track-unit-lh"); + node.classList.remove("track-unit-rh"); + node.classList.remove("track-unit-lf"); + node.classList.remove("track-unit-rf"); + if (type !== "none") { + node.classList.add(`track-unit-${ type }`); + } + } + + private syncTrackUnitType() { + switch (this.trackUnit.getType()) { + case TrackUnitType.Normal: + this.render().classList.remove("track-unit-ghost"); + this.render().classList.remove("track-unit-accent"); + break; + case TrackUnitType.GhostNote: + this.render().classList.add("track-unit-ghost"); + this.render().classList.remove("track-unit-accent"); + break; + case TrackUnitType.Accent: + this.render().classList.remove("track-unit-ghost"); + this.render().classList.add("track-unit-accent"); + break; + case TrackUnitType.GhostNoteAccent: + this.render().classList.add("track-unit-ghost"); + this.render().classList.add("track-unit-accent"); break; } }