Let a filter rule be dragged into place #32

Closed
opened 2026-08-25 15:51:48 +00:00 by jcoffey-dev · 0 comments
Owner

Asked for in #24, alongside the ordering bug #27 fixed: with ~25 rules and only "move one place" buttons, a rule that ends up in the wrong place costs ten clicks to bring back. It was deliberately left out of that PR as an enhancement rather than the bug.

How it works

  • A grip handle on the left of each card arms the drag. The card is draggable only while its own handle is held, so the switch, the name, and the buttons still take a plain click.
  • The card being dragged fades; the card under the pointer draws a 3px line on the edge the rule would land on — top half means above, bottom half means below.
  • The up/down buttons stay exactly as they are. They are the keyboard path, and dragging is an addition rather than a replacement.
  • The drag carries a private application/x-ihasmail-sieve-rule type, so a rule cannot be dropped anywhere else and nothing else can be dropped on the list. Same idiom as the message-to-folder drag in MailboxTree.
  • The reorder itself is reorderRules() in lib/sieve.ts, next to upsertRule from #27, so ordering lives in one place and is unit-testable.

One real bug, found by running it

The guard against dropping a rule onto itself first read React state. dragstart and the first dragover can arrive in the same frame, before a re-render, so the guard read null and drew a drop line on the card being dragged. It now reads a ref, set synchronously in onDragStart. There is a test that fires the two events back to back, which fails against the state-based version.

Verified

Driven in a real browser against npm run dev:mock: handles present, draggable arming only the pressed card, the dragged card fading, the drop line on the correct edge (computed style confirmed as inset 0 -3px 0 0, bottom only), both directions reordering correctly, self-drop doing nothing, and every class and attribute cleared afterwards.

npm run typecheck, npm test — 194 web + 88 server, all passing. Five new component tests cover arming, both drop directions, the same-frame race, and a foreign drag type being ignored; four of them fail without the change.

Not included: touch dragging. HTML5 drag and drop is mouse-only, so on a phone the up/down buttons remain the way to reorder. Worth its own change if it matters.

🤖 Generated with Claude Code

Merged 2026-08-25 as coffey-labs/ihasmail@9a2f355a4a

Rebuilt from: git history, session transcript.

Asked for in #24, alongside the ordering bug #27 fixed: with ~25 rules and only "move one place" buttons, a rule that ends up in the wrong place costs ten clicks to bring back. It was deliberately left out of that PR as an enhancement rather than the bug. ## How it works - A grip handle on the left of each card arms the drag. The card is `draggable` only while its own handle is held, so the switch, the name, and the buttons still take a plain click. - The card being dragged fades; the card under the pointer draws a 3px line on the edge the rule would land on — top half means above, bottom half means below. - The up/down buttons stay exactly as they are. They are the keyboard path, and dragging is an addition rather than a replacement. - The drag carries a private `application/x-ihasmail-sieve-rule` type, so a rule cannot be dropped anywhere else and nothing else can be dropped on the list. Same idiom as the message-to-folder drag in `MailboxTree`. - The reorder itself is `reorderRules()` in `lib/sieve.ts`, next to `upsertRule` from #27, so ordering lives in one place and is unit-testable. ## One real bug, found by running it The guard against dropping a rule onto itself first read React state. `dragstart` and the first `dragover` can arrive in the same frame, before a re-render, so the guard read `null` and drew a drop line on the card being dragged. It now reads a ref, set synchronously in `onDragStart`. There is a test that fires the two events back to back, which fails against the state-based version. ## Verified Driven in a real browser against `npm run dev:mock`: handles present, `draggable` arming only the pressed card, the dragged card fading, the drop line on the correct edge (computed style confirmed as `inset 0 -3px 0 0`, bottom only), both directions reordering correctly, self-drop doing nothing, and every class and attribute cleared afterwards. `npm run typecheck`, `npm test` — 194 web + 88 server, all passing. Five new component tests cover arming, both drop directions, the same-frame race, and a foreign drag type being ignored; four of them fail without the change. Not included: touch dragging. HTML5 drag and drop is mouse-only, so on a phone the up/down buttons remain the way to reorder. Worth its own change if it matters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) **Merged** 2026-08-25 as coffey-labs/ihasmail@9a2f355a4a16 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.