docs: amend plan to reflect code-review fixes
Tasks 8/10/11 received review fixes during execution; the plan's code blocks are updated to match what shipped: - Task 8: drag controller handles pointercancel + idempotent start. - Task 10: palette pieces are plain spans + svelte-ignore (no focusable-but-not-operable role/tabindex). - Task 11: phantom-load effect keyed on gameId; drag ghost gated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -873,6 +873,12 @@ function makeDrag() {
|
||||
let suppressClickOn: Square | null = null;
|
||||
const THRESHOLD = 6;
|
||||
|
||||
function detach() {
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
window.removeEventListener('pointercancel', onCancel);
|
||||
}
|
||||
|
||||
function onMove(e: PointerEvent) {
|
||||
if (!state.active) return;
|
||||
state.x = e.clientX;
|
||||
@@ -882,15 +888,23 @@ function makeDrag() {
|
||||
}
|
||||
}
|
||||
|
||||
// pointercancel fires instead of pointerup when the browser/OS takes over
|
||||
// the gesture (common on touch). Abort the drag: clean up, drop nothing.
|
||||
function onCancel() {
|
||||
detach();
|
||||
state.active = null;
|
||||
state.moved = false;
|
||||
}
|
||||
|
||||
function onUp(e: PointerEvent) {
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
detach();
|
||||
const src = state.active;
|
||||
const wasDrag = state.moved;
|
||||
state.active = null;
|
||||
state.moved = false;
|
||||
if (!src || !wasDrag) return; // a tap — the board click handler deals with it
|
||||
|
||||
// elementFromPoint returns null off-viewport — treated as an off-board drop.
|
||||
const el = document.elementFromPoint(e.clientX, e.clientY);
|
||||
const sqEl = el?.closest('[data-square]') as HTMLElement | null;
|
||||
const target = sqEl?.dataset.square as Square | undefined;
|
||||
@@ -907,6 +921,7 @@ function makeDrag() {
|
||||
}
|
||||
|
||||
function start(src: DragSource, e: PointerEvent) {
|
||||
detach(); // idempotency — drop any listeners from an unfinished prior drag
|
||||
suppressClickOn = null;
|
||||
state.active = src;
|
||||
state.x = startX = e.clientX;
|
||||
@@ -914,6 +929,7 @@ function makeDrag() {
|
||||
state.moved = false;
|
||||
window.addEventListener('pointermove', onMove);
|
||||
window.addEventListener('pointerup', onUp);
|
||||
window.addEventListener('pointercancel', onCancel);
|
||||
}
|
||||
|
||||
/** The board calls this first in its square-click handler. */
|
||||
@@ -1095,11 +1111,11 @@ Create `packages/client/src/lib/PhantomPalette.svelte`:
|
||||
<span class="hint muted">Drag onto your board — your guess of where the opponent is.</span>
|
||||
<div class="pieces">
|
||||
{#each TYPES as t (t)}
|
||||
<!-- Pointer-only drag source — same deliberate a11y trade-off as the
|
||||
phantom spans in Board.svelte (no keyboard drag interaction). -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<span
|
||||
class="pp pp-{oppColor}"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label={`place ${t}`}
|
||||
onpointerdown={(e) => { e.preventDefault(); phantomDrag.start({ kind: 'palette', type: t }, e); }}
|
||||
>{pieceGlyph({ color: oppColor, type: t })}</span>
|
||||
{/each}
|
||||
@@ -1183,14 +1199,17 @@ After the existing `const turnLabel = ...` derived block, add:
|
||||
return a && phantomDrag.state.moved ? a.type : null;
|
||||
});
|
||||
|
||||
// Load the phantom layer once `you` is known (blind games only).
|
||||
let phantomsLoaded = $state(false);
|
||||
// Load the phantom layer when `you` is known (blind games only). Keyed on
|
||||
// gameId — like the connection effect — so it reloads if this <Game>
|
||||
// instance is reused for a different game without a remount.
|
||||
let loadedFor: string | null = $state(null);
|
||||
$effect(() => {
|
||||
if (phantomsLoaded) return;
|
||||
const id = gameId;
|
||||
const you = game.state.you;
|
||||
if (loadedFor === id) return;
|
||||
if (you && game.state.mode === 'blind') {
|
||||
untrack(() => phantoms.loadForGame(gameId, you));
|
||||
phantomsLoaded = true;
|
||||
untrack(() => phantoms.loadForGame(id, you));
|
||||
loadedFor = id;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1234,7 +1253,7 @@ Replace the `<div class="board-area">...</div>` block with:
|
||||
Immediately after the closing `</div>` of `<div class="game-layout" ...>` (and before the `{#if pendingPromotion ...}` block), add:
|
||||
|
||||
```svelte
|
||||
{#if dragGhost}
|
||||
{#if phantomLayerEnabled && dragGhost}
|
||||
<div
|
||||
class="drag-ghost piece-{oppColor}"
|
||||
style="left: {phantomDrag.state.x}px; top: {phantomDrag.state.y}px;"
|
||||
|
||||
Reference in New Issue
Block a user