fix(ui): ArrangementDialog drag whole box, persist changes across switch, hover highlight

- Remove drag handle restriction — entire pill box is now draggable
- Keep local copy of arrangements so edits survive switching between
  arrangements (was reading stale props after switch-back)
- Highlight corresponding left pill when hovering right lyric preview
This commit is contained in:
Thorsten Bus 2026-03-29 16:34:30 +02:00
parent 78b8fc2e3d
commit 852231ae01

View file

@ -89,20 +89,40 @@ async function assignSong() {
/* ── State ── */
// Local copy of arrangements so changes survive switching between arrangements
const localArrangements = ref(JSON.parse(JSON.stringify(props.arrangements)))
watch(
() => props.arrangements,
(newArr) => {
// Merge server updates (e.g. after create/clone/delete) but keep local edits for existing arrangements
const localById = Object.fromEntries(localArrangements.value.map((a) => [a.id, a]))
localArrangements.value = newArr.map((a) => localById[a.id] ?? JSON.parse(JSON.stringify(a)))
// Add any new arrangements not in local
for (const a of newArr) {
if (!localById[a.id]) {
localArrangements.value.push(JSON.parse(JSON.stringify(a)))
}
}
},
{ deep: true },
)
const currentArrangementId = ref(
props.selectedArrangementId ?? props.arrangements.find((a) => a.is_default)?.id ?? props.arrangements[0]?.id ?? null,
)
const currentArrangement = computed(() =>
props.arrangements.find((a) => a.id === Number(currentArrangementId.value)) ?? null,
localArrangements.value.find((a) => a.id === Number(currentArrangementId.value)) ?? null,
)
const arrangementGroups = ref([])
const hoveredIndex = ref(null)
watch(
currentArrangementId,
(id) => {
const arr = props.arrangements.find((a) => a.id === Number(id))
const arr = localArrangements.value.find((a) => a.id === Number(id))
if (arr?.groups?.length) {
arrangementGroups.value = arr.groups.map((g, i) => ({ ...g, _uid: `${g.id}-${i}-${Date.now()}` }))
} else {
@ -212,6 +232,16 @@ function deleteArrangement() {
function saveArrangement() {
if (!currentArrangement.value) return
// Update local arrangements copy so changes survive switching
const localArr = localArrangements.value.find((a) => a.id === currentArrangement.value.id)
if (localArr) {
localArr.groups = arrangementGroups.value.map((g, i) => ({
...g,
order: i + 1,
}))
}
router.put(
`/arrangements/${currentArrangement.value.id}`,
{
@ -454,7 +484,6 @@ function closeOnBackdrop(e) {
ghost-class="drag-ghost"
chosen-class="drag-chosen"
drag-class="drag-active"
handle=".drag-handle"
class="flex flex-col gap-2"
@end="saveArrangement"
>
@ -462,23 +491,13 @@ function closeOnBackdrop(e) {
v-for="(element, index) in arrangementGroups"
:key="element._uid"
data-testid="arrangement-pill"
class="flex items-center gap-2 rounded-lg border-2 px-3 py-2"
class="flex cursor-grab items-center gap-2 rounded-lg border-2 px-3 py-2"
:class="{ 'ring-2 ring-indigo-400 ring-offset-1': hoveredIndex === index }"
:style="{
borderColor: element.color ?? '#6b7280',
backgroundColor: (element.color ?? '#6b7280') + '20',
}"
>
<!-- Drag handle -->
<span class="drag-handle cursor-grab text-gray-400 hover:text-gray-600">
<svg
class="h-4 w-4"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M7 2a2 2 0 10.001 4.001A2 2 0 007 2zm0 6a2 2 0 10.001 4.001A2 2 0 007 8zm0 6a2 2 0 10.001 4.001A2 2 0 007 14zm6-8a2 2 0 10-.001-4.001A2 2 0 0013 6zm0 2a2 2 0 10.001 4.001A2 2 0 0013 8zm0 6a2 2 0 10.001 4.001A2 2 0 0013 14z" />
</svg>
</span>
<!-- Group name -->
<span class="flex-1 text-sm font-medium">
{{ element.name }}
@ -562,10 +581,13 @@ function closeOnBackdrop(e) {
</h4>
<div
v-for="element in arrangementGroups"
v-for="(element, index) in arrangementGroups"
:key="element._uid"
class="rounded-r-lg border-l-4 bg-white p-3 shadow-sm"
class="rounded-r-lg border-l-4 bg-white p-3 shadow-sm transition-shadow"
:class="{ 'ring-2 ring-indigo-400 ring-offset-1': hoveredIndex === index }"
:style="{ borderColor: element.color ?? '#6b7280' }"
@mouseenter="hoveredIndex = index"
@mouseleave="hoveredIndex = null"
>
<!-- Group name pill -->
<span