update
This commit is contained in:
1105
package-lock.json
generated
1105
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -15,22 +15,21 @@
|
||||
"author": "Daniel Ledda",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@djledda/ladder": "^1.1.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"pinia": "^2.0.32",
|
||||
"sass": "^1.58.3",
|
||||
"vue": "^3.2.47",
|
||||
"zod": "^3.21.4"
|
||||
"zod": "^3.21.4",
|
||||
"vue": "^3.4.21",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.40",
|
||||
"@types/node": "^20.12.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.26.0",
|
||||
"@typescript-eslint/parser": "^5.26.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-plugin-vue": "^9.11.0",
|
||||
"typescript": "^5.0.4",
|
||||
"vite": "^4.3.0"
|
||||
"typescript": "^5.4.3",
|
||||
"vite": "^5.2.7"
|
||||
}
|
||||
}
|
||||
|
||||
12
src/Beat.ts
12
src/Beat.ts
@@ -117,17 +117,17 @@ export class Beat extends EffectScoped {
|
||||
return track;
|
||||
}
|
||||
|
||||
swapTracksByIndices(trackIndex1: number, trackIndex2: number): void {
|
||||
const track1 = this.getTrackByIndex(trackIndex1);
|
||||
const track2 = this.getTrackByIndex(trackIndex2);
|
||||
this.tracks.value[trackIndex1] = track2;
|
||||
this.tracks.value[trackIndex2] = track1;
|
||||
insertAt(trackIndex: number, newIndex: number): void {
|
||||
const track = this.getTrackByIndex(trackIndex);
|
||||
this.tracks.value.splice(trackIndex, 1);
|
||||
this.tracks.value = this.tracks.value.slice(0, newIndex).concat([track]).concat(this.tracks.value.slice(newIndex));
|
||||
triggerRef(this.tracks);
|
||||
}
|
||||
|
||||
addTrack(options?: Omit<TrackInitOptions, 'manager'>): Track | null {
|
||||
const optionsResolved = {
|
||||
manager: this.manager,
|
||||
bars: this.barCount.value,
|
||||
barCount: this.barCount.value,
|
||||
isLooping: this.globalIsLooping.value,
|
||||
loopLength: this.globalLoopLength.value,
|
||||
...options,
|
||||
|
||||
@@ -9,7 +9,7 @@ export const DrumSlayerSaveSchema = z.object({
|
||||
});
|
||||
export type DrumSlayerSave = z.infer<typeof DrumSlayerSaveSchema>;
|
||||
|
||||
function defaultMainBeatGroup(manager: BeatManager): Beat {
|
||||
function createDefaultMainBeatGroup(manager: BeatManager): Beat {
|
||||
const defaultSettings = {
|
||||
manager,
|
||||
barCount: 2,
|
||||
@@ -33,14 +33,14 @@ export function createBeatStore() {
|
||||
},
|
||||
};
|
||||
|
||||
const beats = shallowRef<Beat[]>([ defaultMainBeatGroup(manager) ]);
|
||||
const beats = shallowRef<Beat[]>([ createDefaultMainBeatGroup(manager) ]);
|
||||
const activeBeatIndex = ref(0);
|
||||
const activeBeat = computed<Beat | null>(() => beats.value[activeBeatIndex.value] ?? null);
|
||||
const orientation = ref<"horizontal" | "vertical">("horizontal");
|
||||
|
||||
function resetActiveBeat(): void {
|
||||
const current = activeBeat.value;
|
||||
beats.value[activeBeatIndex.value] = defaultMainBeatGroup(manager);
|
||||
beats.value[activeBeatIndex.value] = createDefaultMainBeatGroup(manager);
|
||||
current?.destroy();
|
||||
triggerRef(beats);
|
||||
}
|
||||
@@ -52,10 +52,11 @@ export function createBeatStore() {
|
||||
triggerRef(beats);
|
||||
}
|
||||
|
||||
function addNewBeat(): void {
|
||||
const newBeat = defaultMainBeatGroup(manager);
|
||||
beats.value.push(newBeat);
|
||||
function addNewBeat(): number {
|
||||
const newBeat = createDefaultMainBeatGroup(manager);
|
||||
const newIndex = beats.value.push(newBeat);
|
||||
triggerRef(beats);
|
||||
return newIndex - 1;
|
||||
}
|
||||
|
||||
function save(destination: "localStorage"): void {
|
||||
@@ -111,7 +112,7 @@ export function createBeatStore() {
|
||||
const savedItem = localStorage.getItem("drum-slayer-save");
|
||||
if (savedItem) {
|
||||
const serial = JSON.parse(savedItem);
|
||||
beats.value = [defaultMainBeatGroup(manager)];
|
||||
beats.value = [createDefaultMainBeatGroup(manager)];
|
||||
loadFromSave(serial);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ export type TrackInitOptions = {
|
||||
barCount?: number,
|
||||
units?: TrackUnit[],
|
||||
name?: string,
|
||||
bars?: number,
|
||||
isLooping?: boolean,
|
||||
loopLength?: number,
|
||||
};
|
||||
@@ -59,10 +58,10 @@ export function deserialise(serial: unknown, manager: BeatManager) {
|
||||
}));
|
||||
return Track.asScoped({
|
||||
manager,
|
||||
bars: beat.barCount,
|
||||
isLooping: beat.looping,
|
||||
loopLength: beat.loopLength,
|
||||
name: beat.name,
|
||||
barCount: serial.barCount,
|
||||
isLooping: serial.looping,
|
||||
loopLength: serial.loopLength,
|
||||
name: serial.name,
|
||||
timeSig: {
|
||||
up: beat.timeSigUp,
|
||||
down: beat.timeSigDown,
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
<div class="beat" :class="{ vertical: false }">
|
||||
<editable-text-field node-type="h3" class="beat-title" v-model="beat!.name.value" />
|
||||
<div class="beat-main-container">
|
||||
<div class="beat-titles-container">
|
||||
<div class="beat-track-title" v-for="title in titles">{{ title }}</div>
|
||||
</div>
|
||||
<div class="beat-track-container">
|
||||
<track-view
|
||||
v-for="(_, i) in beat!.tracks.value"
|
||||
:beat-index="beatIndex"
|
||||
:track-index="i" />
|
||||
<draggable handle=".handle" :list="tracks" @change="onMove" item-key="index">
|
||||
<template #item="{ element }">
|
||||
<div class="beat-line">
|
||||
<editable-text-field class="track-name" v-model="element.track.name.value" />
|
||||
<span class="handle"><icon color="var(--color-ui-neutral-dark)" icon-name="list" /></span>
|
||||
<track-view :beat-index="beatIndex"
|
||||
:track-index="element.index" />
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,6 +24,9 @@
|
||||
import { useAppStateStore } from '@/AppState';
|
||||
import { useBeatStore } from '@/BeatStore';
|
||||
import { computed } from "vue";
|
||||
import Draggable from "vuedraggable";
|
||||
import type { Track } from "@/Track";
|
||||
import Icon from "../Widgets/Icon/Icon.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
beatIndex: number,
|
||||
@@ -31,13 +37,30 @@
|
||||
const { beats } = useBeatStore();
|
||||
const beat = computed(() => beats.value[props.beatIndex] ?? null);
|
||||
|
||||
const titles = computed(() => {
|
||||
const titles = beats.value[props.beatIndex]?.tracks.value.map(track => track.name.value) ?? [];
|
||||
if (props.orientation === 'horizontal') {
|
||||
titles.reverse();
|
||||
const tracks = computed(() => {
|
||||
const trackList = beat.value?.tracks.value ?? [];
|
||||
const length = trackList.length;
|
||||
const list: { index: number, track: Track }[] = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
list.push({
|
||||
index: i,
|
||||
track: trackList[i]!,
|
||||
});
|
||||
}
|
||||
return titles;
|
||||
if (props.orientation === 'horizontal') {
|
||||
list.reverse();
|
||||
}
|
||||
return list;
|
||||
});
|
||||
|
||||
function onMove(evt: { moved: { oldIndex: number, newIndex: number }}) {
|
||||
if (props.orientation === 'horizontal') {
|
||||
beat.value?.insertAt(tracks.value.length - 1 - evt.moved.oldIndex, tracks.value.length - 1 - evt.moved.newIndex);
|
||||
} else {
|
||||
beat.value?.insertAt(evt.moved.oldIndex, evt.moved.newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -72,6 +95,7 @@
|
||||
}
|
||||
|
||||
.beat-track-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -115,5 +139,25 @@
|
||||
writing-mode: vertical-rl;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.handle {
|
||||
color: var(--color-ui-neutral-dark);
|
||||
opacity: 0%;
|
||||
|
||||
&:hover {
|
||||
opacity: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.beat-line {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.track-name {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-add-beat"
|
||||
@click="addNewBeat()">
|
||||
@click="onAddNewBeat()">
|
||||
+
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,6 +103,10 @@
|
||||
saveDirty,
|
||||
} = beatStore;
|
||||
|
||||
function onAddNewBeat() {
|
||||
activeBeatIndex.value = addNewBeat();
|
||||
}
|
||||
|
||||
watch(saveDirty, (dirty) => {
|
||||
if (dirty) {
|
||||
document.title = `${ TITLE } (unsaved changes)`;
|
||||
|
||||
@@ -35,12 +35,20 @@
|
||||
activeStickingType,
|
||||
activeTrackUnitType,
|
||||
selectingUnits,
|
||||
deselectingUnits,
|
||||
deselectingUnts,
|
||||
} = useAppStateStore();
|
||||
const { beats } = useBeatStore();
|
||||
const beat = computed(() => beats.value[props.beatIndex] ?? null);
|
||||
const track = computed(() => beat.value?.tracks.value[props.trackIndex] ?? null);
|
||||
const title = computed(() => track.value?.name);
|
||||
|
||||
function swapUp() {
|
||||
beat.value?.swapTracksByIndices(props.trackIndex + 1, props.trackIndex);
|
||||
}
|
||||
|
||||
function swapDown() {
|
||||
beat.value?.swapTracksByIndices(props.trackIndex, props.trackIndex - 1);
|
||||
}
|
||||
|
||||
|
||||
const trackUnits = computed(() => {
|
||||
const units = [];
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div class="track-settings" v-if="track && beat">
|
||||
<div class="track-settings-title-container">
|
||||
<editable-text-field v-model="track!.name.value" />
|
||||
<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="trash" type="secondary" alt="Delete Track" @click="beat!.removeTrack(trackIndex)" />
|
||||
<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)" />
|
||||
<div class="loop-settings">
|
||||
<bool-box label="Loop:" v-model="track!.looping.value" />
|
||||
<bool-box label="Loop:" v-model="track.looping.value" />
|
||||
</div>
|
||||
<div class="loop-settings-option" :class="{ hide: !track!.looping.value }">
|
||||
<number-input v-model="track!.loopLength.value" />
|
||||
<div class="loop-settings-option" :class="{ hide: !track.looping.value }">
|
||||
<number-input v-model="track.loopLength.value" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,7 +22,7 @@
|
||||
import ActionButton from "@/ui/Widgets/ActionButton/ActionButton.vue";
|
||||
import EditableTextField from "@/ui/Widgets/EditableTextField/EditableTextField.vue";
|
||||
import { useBeatStore } from "@/BeatStore";
|
||||
import { computed } from "vue";
|
||||
import { computed, watch } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
beatIndex: number,
|
||||
@@ -32,6 +32,8 @@
|
||||
const { beats } = useBeatStore();
|
||||
const beat = computed(() => beats.value[props.beatIndex] ?? null);
|
||||
const track = computed(() => beat.value?.tracks.value[props.trackIndex] ?? null);
|
||||
|
||||
watch(track, () => console.log(track.value));
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user