This last week was spent implementing todo reordering. While the main view only shows you a single app, there is a "backend" page that shows you all tasks. I want to be able to reorder this easily. I basically just followed the HTMX Sortable example. Mine is a little more simplified:

htmx.onLoad(() => {
    const list = document.getElementById('pendingTodos');
    if (list) {
        const sortableInstance = new Sortable(list, {
            animation: 150,
            ghostClass: 'sortable-ghost',
            chosenClass: 'sortable-chosen',
            dragClass: 'sortable-drag',

            // Disable sorting on the `end` event
            onEnd: function (evt) {
                this.option('disabled', true);
            },
        });

        list.addEventListener('htmx:afterSwap', function () {
            sortableInstance.option('disabled', false);
        });
    }
});

I load this script when you land on the todos page. When Sortable emits an end event, that triggers the request to save the reorder. I use hx-include to include the hidden value of the todo ID in the request. This is sent to the backend as an array of IDs in the expected order.

<div
    hx-post={`/app/list/${list?.id}/todos/reorder`}
    hx-trigger="end"
    hx-include="#pendingTodos [name='id']"
>
    <GroupedTodoList todos={list?.todos ?? []} />
</div>

Perhaps naively, I take that array and just brute force update every todo. I don't try and get fancy and only update the todos that are affected by a move. I'm sure there are better ways. I found a Drizzle ORM guide on making multiple updates at once. I get what it's doing, but I wonder if there is a more efficient way.

const sqlChunks: SQL[] = [];
sqlChunks.push(sql`(case`);
ids.forEach((id, i) => {
    sqlChunks.push(sql`when ${todos.id} = ${id} then ${i + 1}`);
    ids.push(id);
});
sqlChunks.push(sql`end)`);
const finalSql: SQL = sql.join(sqlChunks, sql.raw(' '));
console.log(finalSql);
await db
    .update(todos)
    .set({ position: finalSql })
    .where(inArray(todos.id, ids));

Right now, I don't allow ordering of completed todos. So I need to figure out how to handle position in that case. Possibly clearing out position when a todo is marked completed. Or making separate queries for pending and completed todos so I can order them differently. Not sure yet.

I also want to handle reordering multiple todos at once. This will require some thought though. Eventually, I want inline editing and some menu options on each todo and I don't want that to interfere with selection and reordering. I may need to reconsider some things.

Still no screenshots. I'm just using Pico CSS and I'm not happy with how it looks. I might spend some time this coming week to play with some design ideas of my own.