This commit is contained in:
Timothy Jaeryang Baek
2026-03-15 21:09:55 -05:00
parent 3dea69f658
commit 753589e51c
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import { createEventDispatcher, getContext, onDestroy, onMount, tick } from 'svelte'; import { createEventDispatcher, getContext, onMount, onDestroy, tick } from 'svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
import { models } from '$lib/stores'; import { models } from '$lib/stores';
@@ -13,22 +13,20 @@
let sortable = null; let sortable = null;
let modelListElement = null; let modelListElement = null;
const positionChangeHandler = () => { const positionChangeHandler = (event) => {
// Read new order from DOM const { oldIndex, newIndex, item } = event;
const newOrder = Array.from(modelListElement.children).map((child) =>
child.id.replace('model-item-', '')
);
// Revert SortableJS DOM manipulation so Svelte stays in control of the DOM // Revert SortableJS's DOM manipulation so Svelte doesn't get out of sync.
if (sortable) { // Svelte expects the DOM to match its virtual DOM before it applies state updates.
sortable.sort( const parent = item.parentNode;
modelIds.map((id) => `model-item-${id}`), const target = parent.children[oldIndex < newIndex ? oldIndex : oldIndex + 1];
true parent.insertBefore(item, target);
);
}
// Update reactive data — Svelte will re-render with the new order // Now apply the logical state update, letting Svelte handle the real DOM move.
modelIds = newOrder; const updatedIds = [...modelIds];
const [movedId] = updatedIds.splice(oldIndex, 1);
updatedIds.splice(newIndex, 0, movedId);
modelIds = updatedIds;
}; };
const initSortable = () => { const initSortable = () => {
@@ -41,15 +39,20 @@
sortable = new Sortable(modelListElement, { sortable = new Sortable(modelListElement, {
animation: 150, animation: 150,
handle: '.model-item-handle', handle: '.model-item-handle',
onUpdate: async (event) => { onUpdate: (event) => {
positionChangeHandler(); positionChangeHandler(event);
} }
}); });
} }
}; };
$: if (modelIds && modelListElement) {
tick().then(() => {
initSortable();
});
}
onMount(() => { onMount(() => {
// Wait a tick for the {#if} block to render and bind modelListElement
tick().then(() => { tick().then(() => {
initSortable(); initSortable();
}); });