update
This commit is contained in:
@@ -1,7 +0,0 @@
|
|||||||
export default {
|
|
||||||
extends: [
|
|
||||||
// add more generic rulesets here, such as:
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:vue/vue3-recommended',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {inject, type InjectionKey, ref, getCurrentInstance, watch} from "vue";
|
import { inject, type InjectionKey, ref, getCurrentInstance, watch } from "vue";
|
||||||
import { Bound } from "./utils";
|
import { Bound } from "./utils";
|
||||||
|
|
||||||
export type UITool =
|
export type UITool =
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ export class BeatStore extends Bound {
|
|||||||
activeBeatIndex = ref(0);
|
activeBeatIndex = ref(0);
|
||||||
activeBeat = computed<Beat | null>(() => this.beats.value[this.activeBeatIndex.value] ?? null);
|
activeBeat = computed<Beat | null>(() => this.beats.value[this.activeBeatIndex.value] ?? null);
|
||||||
orientation = ref<"horizontal" | "vertical">("horizontal");
|
orientation = ref<"horizontal" | "vertical">("horizontal");
|
||||||
|
trackSettingsOpen = ref<number | null>(null);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
watch([this.activeBeatIndex, this.orientation, this.beats], () => {
|
watch([this.activeBeatIndex, this.orientation, this.beats], () => {
|
||||||
this.saveDirtyGlobal.value = true;
|
this.saveDirtyGlobal.value = true;
|
||||||
|
this.trackSettingsOpen.value = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const saveInterval = setInterval(() => this.saveDirtyGlobal.value && this.save("localStorage"), 5 * 60 * 1000);
|
const saveInterval = setInterval(() => this.saveDirtyGlobal.value && this.save("localStorage"), 5 * 60 * 1000);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { isPosInt, EffectScoped } from "@/utils";
|
|||||||
import { ref, shallowRef, computed, watch, reactive, triggerRef, type Ref, type ShallowRef } from "vue";
|
import { ref, shallowRef, computed, watch, reactive, triggerRef, type Ref, type ShallowRef } from "vue";
|
||||||
import type { BeatManager } from "./Beat";
|
import type { BeatManager } from "./Beat";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useAppStateStore, type UITool } from "./AppState";
|
import { useAppStateStore } from "./AppState";
|
||||||
|
|
||||||
export type TrackInitOptions = {
|
export type TrackInitOptions = {
|
||||||
visible?: boolean,
|
visible?: boolean,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="beat" class="beat" :class="{ vertical: false }">
|
<div v-if="beat" class="beat">
|
||||||
<editable-text-field node-type="h3" class="beat-title" v-model="beat.name.value" />
|
<editable-text-field node-type="h3" class="beat-title" v-model="beat.name.value" />
|
||||||
<div class="beat-main-container">
|
<div class="beat-main-container">
|
||||||
<div class="beat-track-container" :class="{ dragging }">
|
<div class="beat-track-container" :class="{ dragging }">
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
:sticking-type="type + 1" />
|
:sticking-type="type + 1" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<VueDraggableNext v-else
|
<div v-else class="beat-wrap-row">
|
||||||
|
<VueDraggableNext
|
||||||
@start="dragging = true"
|
@start="dragging = true"
|
||||||
@end="dragging = false"
|
@end="dragging = false"
|
||||||
animation="150"
|
animation="150"
|
||||||
@@ -23,16 +24,11 @@
|
|||||||
item-key="index">
|
item-key="index">
|
||||||
<transition-group name="fade">
|
<transition-group name="fade">
|
||||||
<template v-for="element in tracks">
|
<template v-for="element in tracks">
|
||||||
<div v-if="element.track.visible.value" class="beat-line" :key="element.index">
|
<div v-if="element.track.visible.value" :key="element.index">
|
||||||
|
<div class="beat-line">
|
||||||
<div class="track-actions">
|
<div class="track-actions">
|
||||||
<span class="track-action">
|
<span @click="toggleTrackSettings(element.index)" class="track-action">
|
||||||
<icon color="var(--color-ui-neutral-dark)"
|
<icon color="var(--color-ui-neutral-dark)" :icon-name="trackSettingsOpen === element.index ? 'up' : 'down'" />
|
||||||
@click="element.track.visible.value = !element.track.visible.value"
|
|
||||||
:icon-name="element.track.visible.value ? 'eye' : 'eyeOff'" />
|
|
||||||
</span>
|
|
||||||
<span class="track-action"
|
|
||||||
@click="applyToolToTrack(element.track)">
|
|
||||||
<icon color="var(--color-ui-neutral-dark)" icon-name="paintBucket" />
|
|
||||||
</span>
|
</span>
|
||||||
<span class="handle track-action">
|
<span class="handle track-action">
|
||||||
<icon color="var(--color-ui-neutral-dark)" icon-name="list" />
|
<icon color="var(--color-ui-neutral-dark)" icon-name="list" />
|
||||||
@@ -42,10 +38,24 @@
|
|||||||
<track-view :beat-index="beatIndex"
|
<track-view :beat-index="beatIndex"
|
||||||
:track-index="element.index" />
|
:track-index="element.index" />
|
||||||
</div>
|
</div>
|
||||||
|
<track-settings-view v-show="trackSettingsOpen === element.index" :beat-index="beatIndex" :track-index="element.index" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</VueDraggableNext>
|
</VueDraggableNext>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
|
<div v-for="i in beat.barCount.value - 1" class="beat-wrap-row">
|
||||||
|
<template v-for="(element, j) in tracks" :key="j">
|
||||||
|
<div v-if="element.track.visible.value" class="beat-line" :key="element.index">
|
||||||
|
<track-view :beat-index="beatIndex"
|
||||||
|
:bars="{ start: i, count: 1 }"
|
||||||
|
:track-index="element.index" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -60,13 +70,14 @@
|
|||||||
import { TrackUnitStickingTypeList, type Track } from "@/Track";
|
import { TrackUnitStickingTypeList, type Track } from "@/Track";
|
||||||
import Icon from "@/ui/Widgets/Icon/Icon.vue";
|
import Icon from "@/ui/Widgets/Icon/Icon.vue";
|
||||||
import { StickingTypeIconMap } from "../TrackUnit/trackUnit";
|
import { StickingTypeIconMap } from "../TrackUnit/trackUnit";
|
||||||
|
import TrackSettingsView from "@/ui/TrackSettings/TrackSettings.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
beatIndex: number,
|
beatIndex: number,
|
||||||
orientation?: "horizontal" | "vertical",
|
orientation?: "horizontal" | "vertical",
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { beats } = useBeatStore();
|
const { beats, trackSettingsOpen } = useBeatStore();
|
||||||
const beat = computed(() => beats.value[props.beatIndex] ?? null);
|
const beat = computed(() => beats.value[props.beatIndex] ?? null);
|
||||||
|
|
||||||
const tracks = computed(() => {
|
const tracks = computed(() => {
|
||||||
@@ -87,6 +98,14 @@
|
|||||||
|
|
||||||
const dragging = ref(false);
|
const dragging = ref(false);
|
||||||
|
|
||||||
|
function toggleTrackSettings(trackIndex: number) {
|
||||||
|
if (trackSettingsOpen.value === trackIndex) {
|
||||||
|
trackSettingsOpen.value = null;
|
||||||
|
} else {
|
||||||
|
trackSettingsOpen.value = trackIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onMove(evt: { moved: { oldIndex: number, newIndex: number }}) {
|
function onMove(evt: { moved: { oldIndex: number, newIndex: number }}) {
|
||||||
if (props.orientation !== 'horizontal') {
|
if (props.orientation !== 'horizontal') {
|
||||||
beat.value?.insertAt(tracks.value.length - 1 - evt.moved.oldIndex, tracks.value.length - 1 - evt.moved.newIndex);
|
beat.value?.insertAt(tracks.value.length - 1 - evt.moved.oldIndex, tracks.value.length - 1 - evt.moved.newIndex);
|
||||||
@@ -110,6 +129,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.beat-wrap-row {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.beat-title {
|
.beat-title {
|
||||||
color: var(--color-title-light);
|
color: var(--color-title-light);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -124,7 +148,7 @@
|
|||||||
|
|
||||||
.beat-main-container {
|
.beat-main-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
white-space: nowrap;
|
//white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-name {
|
.track-name {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
beatIndex: number,
|
beatIndex: number,
|
||||||
locked?: boolean,
|
locked?: boolean,
|
||||||
trackIndex: number,
|
trackIndex: number,
|
||||||
|
bars?: { start: number, count: number },
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -52,8 +53,16 @@
|
|||||||
|
|
||||||
const trackUnits = computed(() => {
|
const trackUnits = computed(() => {
|
||||||
const units = [];
|
const units = [];
|
||||||
if (track.value) {
|
let start = 0;
|
||||||
for (let i = 0; i < track.value.unitCount(); i++) {
|
let end = 0;
|
||||||
|
if (track.value && beat.value) {
|
||||||
|
if (props.bars) {
|
||||||
|
start = beat.value.timeSigUp.value * props.bars.start;
|
||||||
|
end = start + beat.value.timeSigUp.value * props.bars.count;
|
||||||
|
} else {
|
||||||
|
end = track.value.unitCount();
|
||||||
|
}
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
const unit = track.value.getUnitByIndex(i);
|
const unit = track.value.getUnitByIndex(i);
|
||||||
if (unit) {
|
if (unit) {
|
||||||
units.push(unit);
|
units.push(unit);
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="track-settings" v-if="track && beat">
|
<div class="track-settings" v-if="track && beat">
|
||||||
<div class="track-settings-title-container">
|
|
||||||
<editable-text-field v-model="track.name.value" />
|
|
||||||
</div>
|
|
||||||
<div class="track-settings-lower">
|
|
||||||
<action-button icon-name="snowflake" type="secondary" alt="Bake Loops" :disabled="!track.looping.value" @click="track.bakeLoops()" />
|
<action-button icon-name="snowflake" type="secondary" alt="Bake Loops" :disabled="!track.looping.value" @click="track.bakeLoops()" />
|
||||||
<action-button icon-name="trash" type="secondary" alt="Delete Track" @click="beat.removeTrack(trackIndex)" />
|
<action-button icon-name="trash" type="secondary" alt="Delete Track" @click="beat.removeTrack(trackIndex)" />
|
||||||
<div class="loop-settings">
|
<div class="loop-settings">
|
||||||
@@ -12,19 +8,13 @@
|
|||||||
<div class="loop-settings-option" :class="{ hide: !track.looping.value }">
|
<div class="loop-settings-option" :class="{ hide: !track.looping.value }">
|
||||||
<number-input v-model="track.loopLength.value" />
|
<number-input v-model="track.loopLength.value" />
|
||||||
</div>
|
</div>
|
||||||
<div class="visibility">
|
|
||||||
<icon color="var(--color-ui-neutral-dark)" :icon-name="track.visible.value ? 'eye' : 'eyeOff'" @click="track.visible.value = !track.visible.value"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NumberInput from "@/ui/Widgets/NumberInput/NumberInput.vue";
|
import NumberInput from "@/ui/Widgets/NumberInput/NumberInput.vue";
|
||||||
import Icon from "@/ui/Widgets/Icon/Icon.vue";
|
|
||||||
import BoolBox from "@/ui/Widgets/BoolBox/BoolBox.vue";
|
import BoolBox from "@/ui/Widgets/BoolBox/BoolBox.vue";
|
||||||
import ActionButton from "@/ui/Widgets/ActionButton/ActionButton.vue";
|
import ActionButton from "@/ui/Widgets/ActionButton/ActionButton.vue";
|
||||||
import EditableTextField from "@/ui/Widgets/EditableTextField/EditableTextField.vue";
|
|
||||||
import { useBeatStore } from "@/BeatStore";
|
import { useBeatStore } from "@/BeatStore";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
@@ -39,54 +29,39 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.track-settings-title-container input {
|
.track-settings {
|
||||||
min-width: 100%;
|
|
||||||
height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-settings-title-container > div {
|
|
||||||
width: 100%;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0.5em;
|
|
||||||
transition: background-color 200ms;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-settings-title-container > div:hover {
|
|
||||||
background-color: var(--color-ui-neutral-dark-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-settings-lower {
|
|
||||||
height: 3.5em;
|
height: 3.5em;
|
||||||
display: flex;
|
display: flex;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
background-color: var(--color-bg-medium);
|
||||||
.track-settings-lower > * {
|
|
||||||
margin-right: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-settings-lower:last-child {
|
.loop-settings {
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-settings .loop-settings {
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-settings .loop-settings-option.hide {
|
.loop-settings-option.hide {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-settings .loop-settings-option {
|
.loop-settings-option {
|
||||||
flex: auto;
|
flex: auto;
|
||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-right: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.visibility {
|
.visibility {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -13,9 +13,13 @@ import Floppy from "@/assets/svgs/floppy2-fill.svg";
|
|||||||
import Delete from "@/assets/svgs/delete.svg";
|
import Delete from "@/assets/svgs/delete.svg";
|
||||||
import Eye from "@/assets/svgs/eye.svg";
|
import Eye from "@/assets/svgs/eye.svg";
|
||||||
import EyeOff from "@/assets/svgs/eye-off.svg";
|
import EyeOff from "@/assets/svgs/eye-off.svg";
|
||||||
import PaintBucket from '@/assets/svgs/paint-bucket.svg';
|
import PaintBucket from "@/assets/svgs/paint-bucket.svg";
|
||||||
import Sun from '@/assets/svgs/sun.svg';
|
import Sun from "@/assets/svgs/sun.svg";
|
||||||
import Moon from '@/assets/svgs/moon.svg';
|
import Moon from "@/assets/svgs/moon.svg";
|
||||||
|
import Up from "@/assets/svgs/chevron-up.svg";
|
||||||
|
import Down from "@/assets/svgs/chevron-down.svg";
|
||||||
|
import Left from "@/assets/svgs/chevron-left.svg";
|
||||||
|
import Right from "@/assets/svgs/chevron-right.svg";
|
||||||
|
|
||||||
export const IconUrlMap = {
|
export const IconUrlMap = {
|
||||||
arrowClockwise: ArrowClockwise,
|
arrowClockwise: ArrowClockwise,
|
||||||
@@ -36,6 +40,10 @@ export const IconUrlMap = {
|
|||||||
paintBucket: PaintBucket,
|
paintBucket: PaintBucket,
|
||||||
sun: Sun,
|
sun: Sun,
|
||||||
moon: Moon,
|
moon: Moon,
|
||||||
|
up: Up,
|
||||||
|
down: Down,
|
||||||
|
left: Left,
|
||||||
|
right: Right,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type IconName = keyof typeof IconUrlMap;
|
export type IconName = keyof typeof IconUrlMap;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ html, body {
|
|||||||
--color-ui-neutral-dark-hover: #a1a1a1;
|
--color-ui-neutral-dark-hover: #a1a1a1;
|
||||||
--color-ui-neutral-dark-active: #c1c1c1;
|
--color-ui-neutral-dark-active: #c1c1c1;
|
||||||
--color-bg-light: #e0e0e0;
|
--color-bg-light: #e0e0e0;
|
||||||
--color-bg-medium: #fff;
|
--color-bg-medium: #eee;
|
||||||
--color-bg-dark: #fff;
|
--color-bg-dark: #fff;
|
||||||
--color-p-light: #282828;
|
--color-p-light: #282828;
|
||||||
--color-p-light-hover: #fafafa;
|
--color-p-light-hover: #fafafa;
|
||||||
|
|||||||
Reference in New Issue
Block a user