const updates = { selected: null };
if (cell && cell.isBlank) {
const [r, c] = grid_key.split(",").map(Number);
- updates.blank_pending = { r, c, tileIndex: cell.tileIndex, reassign: true };
+ updates.blank_pending = { r, c, tileIndex: cell.tileIndex };
}
this.setState(updates);
return;
delete new_grid[sel.grid_key];
}
- /* Blank tile — always show the modal so the user can pick a letter. */
+ /* Blank tile — show the modal so the user can pick a letter.
+ * If the blank already had a letter (moved from grid), place it
+ * at the destination with that letter first. The modal then just
+ * offers a chance to change it, and cancel is a no-op. */
if (is_blank) {
+ const existing = sel.from === "grid" ? this.state.grid[sel.grid_key] : null;
+ if (existing && existing.letter) {
+ new_grid[grid_key] = { letter: existing.letter,
+ tileIndex: tile_index, isBlank: true };
+ }
this.setState({
grid: new_grid,
rack: new_rack,
<BlankTileModal
onChoose={(l) => this.choose_blank_letter(l)}
onCancel={() => {
- /* Put the blank tile back on the rack if cancelled,
- * but only if it was freshly placed (not a reassignment
- * of a blank already on the grid). */
- if (state.blank_pending.reassign) {
+ const pending = state.blank_pending;
+ const key = pending.r + "," + pending.c;
+ if (state.grid[key]) {
+ /* Tile is already on the grid (moved with an
+ * existing letter, or reassigning in place).
+ * Just dismiss the modal. */
this.setState({ blank_pending: null });
} else {
- const { tileIndex } = state.blank_pending;
- const new_rack = [...state.rack, tileIndex];
+ /* Fresh blank from rack — return it. */
+ const new_rack = [...state.rack, pending.tileIndex];
this.setState({ blank_pending: null, rack: new_rack });
}
}}