315 lines
9.0 KiB
Vue
315 lines
9.0 KiB
Vue
<template>
|
|
<div
|
|
class="root"
|
|
:class="{ 'sidebar-visible': sidebarActive, 'vertical-mode': currentOrientation === 'vertical' }">
|
|
<div class="sidebar">
|
|
<div class="sidebar-left-strip">
|
|
<div v-for="(beat, i) in beats"
|
|
:key="beat.name.value"
|
|
class="sidebar-left-tab"
|
|
:class="{ 'active': i === activeBeatIndex }"
|
|
@click="activeBeatIndex = i">
|
|
{{ beat.name.value }}
|
|
</div>
|
|
<div
|
|
class="sidebar-add-beat"
|
|
@click="onAddNewBeat()">
|
|
+
|
|
</div>
|
|
</div>
|
|
<div class="settings">
|
|
<h1 class="title">{{ title }}</h1>
|
|
<beat-settings :beat-index="activeBeatIndex" />
|
|
</div>
|
|
<div class="sidebar-toggle">
|
|
<div class="buttons">
|
|
<div
|
|
class="quick-access-button"
|
|
:title="`${ sidebarActive ? 'Hide' : 'Show' } sidebar`"
|
|
@click="sidebarActive = !sidebarActive">
|
|
<icon icon-name="list" color="var(--color-ui-neutral-dark)" />
|
|
</div>
|
|
<div
|
|
class="quick-access-button"
|
|
title="Change orientation"
|
|
@click="toggleOrientation">
|
|
<icon icon-name="arrowClockwise" color="var(--color-ui-neutral-dark)" />
|
|
</div>
|
|
<div
|
|
class="quick-access-button"
|
|
title="Bake all tracks"
|
|
@click="bakeAll">
|
|
<icon icon-name="snowflake" color="var(--color-ui-neutral-dark)" />
|
|
</div>
|
|
<div
|
|
class="quick-access-button"
|
|
title="Reset all"
|
|
@click="resetActiveBeat">
|
|
<icon icon-name="trash" color="var(--color-ui-neutral-dark)" />
|
|
</div>
|
|
<div
|
|
class="quick-access-button"
|
|
:class="{ 'unclickable': !saveDirty }"
|
|
:title="saveDirty ? 'Save changes' : 'No unsaved changes'"
|
|
@click="save('localStorage')">
|
|
<icon icon-name="download" color="var(--color-ui-neutral-dark)" />
|
|
</div>
|
|
</div>
|
|
<toolbox class="toolbox" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="beat-stage-container">
|
|
<div class="beat-stage">
|
|
<beat-view
|
|
:beat-index="activeBeatIndex"
|
|
:orientation="currentOrientation" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onBeforeUnmount, onMounted, provide, ref, watch } from "vue";
|
|
import BeatSettings from "@/ui/BeatSettings/BeatSettings.vue";
|
|
import BeatView from "@/ui/Beat/Beat.vue";
|
|
import Icon from "@/ui/Widgets/Icon/Icon.vue";
|
|
import Toolbox from "@/ui/Root/Toolbox.vue";
|
|
import { BeatStoreKey, createBeatStore } from "@/BeatStore";
|
|
import { AppStateStoreKey, createAppStateStore } from "@/AppState";
|
|
|
|
const TITLE = 'Drum Slayer';
|
|
|
|
defineProps<{
|
|
title: string,
|
|
}>();
|
|
|
|
const currentOrientation = ref<'horizontal' | 'vertical'>('horizontal');
|
|
const sidebarActive = ref(false);
|
|
|
|
const appStateStore = createAppStateStore();
|
|
provide(AppStateStoreKey, appStateStore);
|
|
|
|
const beatStore = createBeatStore();
|
|
provide(BeatStoreKey, beatStore);
|
|
|
|
const {
|
|
save,
|
|
resetActiveBeat,
|
|
activeBeatIndex,
|
|
beats,
|
|
addNewBeat,
|
|
bakeAll,
|
|
saveDirty,
|
|
} = beatStore;
|
|
|
|
function onAddNewBeat() {
|
|
activeBeatIndex.value = addNewBeat();
|
|
}
|
|
|
|
watch(saveDirty, (dirty) => {
|
|
if (dirty) {
|
|
document.title = `${ TITLE } (unsaved changes)`;
|
|
} else {
|
|
document.title = TITLE;
|
|
}
|
|
});
|
|
|
|
const mediaQueryList = window.matchMedia("screen and (max-width: 900px)");
|
|
function onMediaChange(event: MediaQueryListEvent | MediaQueryList) {
|
|
sidebarActive.value = event.matches;
|
|
}
|
|
mediaQueryList.addEventListener('change', onMediaChange);
|
|
onMediaChange(mediaQueryList);
|
|
|
|
function windowMouseUp() {
|
|
appStateStore.selectingUnits.value = false;
|
|
appStateStore.deselectingUnits.value = false;
|
|
appStateStore.unitMouseStart.value = null;
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('mouseup', windowMouseUp);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
window.removeEventListener('mouseup', windowMouseUp);
|
|
});
|
|
|
|
function toggleOrientation(): void {
|
|
if (currentOrientation.value === "vertical") {
|
|
currentOrientation.value = "horizontal";
|
|
} else {
|
|
currentOrientation.value = "vertical";
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.root {
|
|
position: relative;
|
|
overflow: hidden;
|
|
color: var(--color-p-light);
|
|
background-color: var(--color-bg-dark);
|
|
height: 100vh;
|
|
align-content: center;
|
|
|
|
.sidebar {
|
|
position: absolute;
|
|
left: -28em;
|
|
width: 30em;
|
|
height: 100vh;
|
|
display: flex;
|
|
transition: left 400ms;
|
|
top: 0;
|
|
}
|
|
|
|
&.sidebar-visible {
|
|
.beat-stage {
|
|
max-width: calc(100vw - 30em);
|
|
}
|
|
|
|
.beat-stage-container {
|
|
left: 30em;
|
|
width: calc(100vw - 30em);
|
|
}
|
|
|
|
.sidebar {
|
|
left: 0;
|
|
}
|
|
}
|
|
|
|
.settings {
|
|
z-index: 1;
|
|
width: 28em;
|
|
background-color: var(--color-bg-light);
|
|
overflow: scroll;
|
|
display: inline-block;
|
|
|
|
.title {
|
|
color: var(--color-title-light);
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.sidebar-toggle {
|
|
z-index: 1;
|
|
height: 100vh;
|
|
min-width: 2em;
|
|
background-color: var(--color-bg-light);
|
|
left: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.buttons {
|
|
flex: 1;
|
|
|
|
.quick-access-button {
|
|
flex: 1;
|
|
right: 0;
|
|
width: 2em;
|
|
height: 2em;
|
|
cursor: pointer;
|
|
margin-bottom: 0.5em;
|
|
|
|
&.unclickable {
|
|
opacity: 50%;
|
|
cursor: auto;
|
|
}
|
|
}
|
|
}
|
|
|
|
.toolbox {
|
|
width: 2em;
|
|
}
|
|
}
|
|
|
|
.beat-stage-container {
|
|
position: absolute;
|
|
height: 100%;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100vw;
|
|
display: flex;
|
|
flex-direction: column;
|
|
transition: left 400ms, width 400ms;
|
|
}
|
|
|
|
.beat-stage {
|
|
position: relative;
|
|
max-height: 100vh;
|
|
margin: auto;
|
|
max-width: 100vw;
|
|
transition: max-width 400ms;
|
|
padding-left: 3em;
|
|
}
|
|
|
|
&.vertical-mode .beat-stage {
|
|
margin: auto auto;
|
|
height: 100vh;
|
|
}
|
|
|
|
.sidebar-left-strip {
|
|
writing-mode: vertical-rl;
|
|
background-color: var(--color-bg-light);
|
|
}
|
|
|
|
.sidebar-left-strip > * {
|
|
display: inline-block;
|
|
}
|
|
|
|
.sidebar-left-tab {
|
|
transform: rotate(-180deg);
|
|
display: inline-block;
|
|
width: 100%;
|
|
padding: 8px 3px 8px 3px;
|
|
}
|
|
|
|
.sidebar-left-tab.active {
|
|
background-color: var(--color-bg-medium);
|
|
display: inline-block;
|
|
}
|
|
|
|
.sidebar-add-beat {
|
|
width: 100%;
|
|
padding: 8px 3px 8px 3px;
|
|
}
|
|
|
|
.sidebar-add-beat:hover,
|
|
.sidebar-left-tab:hover:not(.active) {
|
|
cursor: pointer;
|
|
background-color: var(--color-ui-neutral-dark);
|
|
transition: background-color 200ms;
|
|
}
|
|
|
|
@media screen and (max-width: 900px) {
|
|
&.sidebar-visible .sidebar {
|
|
left: 0;
|
|
width: 100vw;
|
|
}
|
|
.sidebar {
|
|
left: calc(-100vw + 2em);
|
|
width: 100vw;
|
|
}
|
|
.settings {
|
|
width: calc(100vw - 2em);
|
|
}
|
|
.sidebar-visible .beat-stage-container {
|
|
left: 100vw;
|
|
}
|
|
.beat-stage-container {
|
|
left: 0;
|
|
}
|
|
.sidebar-visible .beat-stage {
|
|
max-width: 100vw;
|
|
}
|
|
}
|
|
|
|
* {
|
|
user-drag: none;
|
|
user-select: none;
|
|
}
|
|
}
|
|
</style>
|
|
|