fix(playlists): UAT round 1 — drag lag, an invisible default sort, dialog layout

- The dragged row trailed the cursor by 150ms. The row carried Tailwind's bare
  `transition` utility (copied from DataTable's <tr>), which animates TRANSFORM
  — exactly the property dnd-kit drives from the pointer. transition-colors now,
  and the row being dragged drops dnd-kit's own transition too.
- The list looked sorted by Channel when it was really in its stored order that
  happens to be channel-grouped, and nothing said so. Sorting by "#" ascending
  IS the stored order, so it is now the neutral state: the header shows a real
  arrow for it, cycling off any column lands back on it, and it stays the state
  where drag is enabled and no save is offered.
- A choice dialog with two or more actions stacks vertically. Side by side they
  wrapped raggedly, and the wrapped one read as a lesser second group when the
  two are peers. Cancel goes last, actions run least to most destructive.
This commit is contained in:
2026-07-21 00:03:07 +02:00
parent 6dc7b9268a
commit cdec80547e
3 changed files with 39 additions and 10 deletions
+16 -2
View File
@@ -84,6 +84,7 @@ export function ConfirmProvider({ children }: { children: ReactNode }) {
// autoFocus unconditionally, so a stray Enter on an open dialog executed "Delete user" or
// "Clear audit log" without the user ever looking at it.
const anyDanger = choices.some((c) => c.danger);
const multi = choices.length > 1;
return (
<ChoiceContext.Provider value={ask}>
@@ -95,11 +96,22 @@ export function ConfirmProvider({ children }: { children: ReactNode }) {
maxWidth={choices.length > 1 ? "max-w-md" : "max-w-sm"}
>
<p className="text-sm text-muted leading-relaxed">{state.opts.message}</p>
<div className="flex justify-end gap-2 mt-5 flex-wrap">
{/* One action: the familiar Cancel/Confirm row. Two or more: a column, because side by
side they wrap raggedly and a wrapped button reads as a second, lesser group — and
these are peers. Ordered least to most destructive, top to bottom. */}
<div
className={
multi
? "flex flex-col gap-2 mt-5"
: "flex justify-end gap-2 mt-5 flex-wrap"
}
>
<button
autoFocus={anyDanger}
onClick={() => close(null)}
className="px-4 py-2 rounded-lg text-sm border border-border text-muted hover:text-fg hover:bg-surface transition"
className={`px-4 py-2 rounded-lg text-sm border border-border text-muted hover:text-fg hover:bg-surface transition ${
multi ? "w-full order-last" : ""
}`}
>
{state.opts.cancelLabel ?? t("common.cancel")}
</button>
@@ -109,6 +121,8 @@ export function ConfirmProvider({ children }: { children: ReactNode }) {
autoFocus={!anyDanger && i === choices.length - 1}
onClick={() => close(c.id)}
className={`px-4 py-2 rounded-lg text-sm font-semibold transition ${
multi ? "w-full" : ""
} ${
c.danger
? "bg-red-500 text-white hover:bg-red-600"
: "bg-accent text-accent-fg hover:opacity-90"