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.