fix(ui): resolve transition key conflicts in reorderable lists

Add listKeyPrefix prop to ensure unique keys across list instances
Remove enter/leave transition styles to prevent animation glitches
This commit is contained in:
2026-05-01 12:49:08 +08:00
parent 239a2ec3b5
commit bd068ce2f6
3 changed files with 16 additions and 23 deletions

View File

@@ -5,10 +5,12 @@ const props = withDefaults(defineProps<{
items: T[]; items: T[];
itemKey: (item: T) => string | number; itemKey: (item: T) => string | number;
itemLabel: (item: T) => string; itemLabel: (item: T) => string;
listKeyPrefix?: string;
disabled?: boolean; disabled?: boolean;
handleLabel: (name: string) => string; handleLabel: (name: string) => string;
handleTitle: string; handleTitle: string;
}>(), { }>(), {
listKeyPrefix: '',
disabled: false disabled: false
}); });
@@ -28,6 +30,10 @@ function keyFor(item: T): string | number {
return props.itemKey(item); return props.itemKey(item);
} }
function transitionKeyFor(item: T): string {
return props.listKeyPrefix ? `${props.listKeyPrefix}:${String(keyFor(item))}` : String(keyFor(item));
}
function sameKey(first: string | number, second: string | number): boolean { function sameKey(first: string | number, second: string | number): boolean {
return String(first) === String(second); return String(first) === String(second);
} }
@@ -181,7 +187,7 @@ function handleKeydown(item: T, event: KeyboardEvent) {
<TransitionGroup name="reorderable-list" tag="ul" class="row-list reorderable-list"> <TransitionGroup name="reorderable-list" tag="ul" class="row-list reorderable-list">
<li <li
v-for="item in items" v-for="item in items"
:key="keyFor(item)" :key="transitionKeyFor(item)"
class="reorderable-row" class="reorderable-row"
:class="{ :class="{
'is-dragging': draggingKey === keyFor(item), 'is-dragging': draggingKey === keyFor(item),

View File

@@ -1061,24 +1061,8 @@ button:disabled,
bottom: -2px; bottom: -2px;
} }
.reorderable-list-move, .reorderable-list-move {
.reorderable-list-enter-active, transition: transform 0.18s ease;
.reorderable-list-leave-active {
transition:
opacity 0.18s ease,
transform 0.18s ease;
}
.reorderable-list-enter-from,
.reorderable-list-leave-to {
opacity: 0;
transform: translateY(6px);
}
.reorderable-list-leave-active {
position: absolute;
right: 0;
left: 0;
} }
.drag-handle { .drag-handle {
@@ -1132,15 +1116,11 @@ button:disabled,
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.reorderable-row, .reorderable-row,
.reorderable-list-move, .reorderable-list-move,
.reorderable-list-enter-active,
.reorderable-list-leave-active,
.drag-handle { .drag-handle {
transition: none; transition: none;
} }
.reorderable-row.is-dragging, .reorderable-row.is-dragging,
.reorderable-list-enter-from,
.reorderable-list-leave-to,
.drag-handle:active { .drag-handle:active {
transform: none; transform: none;
} }

View File

@@ -563,6 +563,7 @@ onMounted(() => {
:items="checklistRows" :items="checklistRows"
:item-key="checklistKey" :item-key="checklistKey"
:item-label="checklistLabel" :item-label="checklistLabel"
list-key-prefix="checklist"
:disabled="busy" :disabled="busy"
:handle-label="dragSortLabel" :handle-label="dragSortLabel"
:handle-title="t('pages.admin.dragSortTitle')" :handle-title="t('pages.admin.dragSortTitle')"
@@ -611,6 +612,7 @@ onMounted(() => {
:items="configRows" :items="configRows"
:item-key="configKey" :item-key="configKey"
:item-label="configLabel" :item-label="configLabel"
:list-key-prefix="`config-${activeConfigType}`"
:disabled="busy" :disabled="busy"
:handle-label="dragSortLabel" :handle-label="dragSortLabel"
:handle-title="t('pages.admin.dragSortTitle')" :handle-title="t('pages.admin.dragSortTitle')"
@@ -662,6 +664,7 @@ onMounted(() => {
:items="languageRows" :items="languageRows"
:item-key="languageKey" :item-key="languageKey"
:item-label="languageLabel" :item-label="languageLabel"
list-key-prefix="languages"
:disabled="busy" :disabled="busy"
:handle-label="dragSortLabel" :handle-label="dragSortLabel"
:handle-title="t('pages.admin.dragSortTitle')" :handle-title="t('pages.admin.dragSortTitle')"
@@ -690,6 +693,7 @@ onMounted(() => {
:items="pokemonRows" :items="pokemonRows"
:item-key="pokemonKey" :item-key="pokemonKey"
:item-label="pokemonLabel" :item-label="pokemonLabel"
list-key-prefix="pokemon"
:disabled="busy" :disabled="busy"
:handle-label="dragSortLabel" :handle-label="dragSortLabel"
:handle-title="t('pages.admin.dragSortTitle')" :handle-title="t('pages.admin.dragSortTitle')"
@@ -714,6 +718,7 @@ onMounted(() => {
:items="itemRows" :items="itemRows"
:item-key="itemKey" :item-key="itemKey"
:item-label="itemLabel" :item-label="itemLabel"
list-key-prefix="items"
:disabled="busy" :disabled="busy"
:handle-label="dragSortLabel" :handle-label="dragSortLabel"
:handle-title="t('pages.admin.dragSortTitle')" :handle-title="t('pages.admin.dragSortTitle')"
@@ -738,6 +743,7 @@ onMounted(() => {
:items="recipeRows" :items="recipeRows"
:item-key="recipeKey" :item-key="recipeKey"
:item-label="recipeLabel" :item-label="recipeLabel"
list-key-prefix="recipes"
:disabled="busy" :disabled="busy"
:handle-label="dragSortLabel" :handle-label="dragSortLabel"
:handle-title="t('pages.admin.dragSortTitle')" :handle-title="t('pages.admin.dragSortTitle')"
@@ -762,6 +768,7 @@ onMounted(() => {
:items="habitatRows" :items="habitatRows"
:item-key="habitatKey" :item-key="habitatKey"
:item-label="habitatLabel" :item-label="habitatLabel"
list-key-prefix="habitats"
:disabled="busy" :disabled="busy"
:handle-label="dragSortLabel" :handle-label="dragSortLabel"
:handle-title="t('pages.admin.dragSortTitle')" :handle-title="t('pages.admin.dragSortTitle')"