Make domino move hover only

Update DominoRetilingPuzzle UI to:
- hide the selected domino from the board
- use a hover-based placement indicator instead of dashed outlines
- trigger placements on hover-and-click interaction (described in code)

X-Lovable-Edit-ID: edt-3461c9a0-2f9d-48af-acf7-3a0962c81a8f
This commit is contained in:
gpt-engineer-app[bot] 2025-12-08 09:33:01 +00:00
commit 5671af101c

View file

@ -2,7 +2,7 @@ import React, { useState, useCallback, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { CheckCircle, XCircle, Info, ChevronDown, ChevronUp, RotateCcw, Shuffle, Lightbulb } from 'lucide-react';
import { CheckCircle, ChevronDown, ChevronUp, Shuffle, Lightbulb } from 'lucide-react';
import SocialShare from '@/components/SocialShare';
interface Domino {
@ -22,6 +22,7 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
const [showInstructions, setShowInstructions] = useState(false);
const [moveCount, setMoveCount] = useState(0);
const [showHint, setShowHint] = useState(false);
const [hoverPlacement, setHoverPlacement] = useState<{ row: number; col: number; isHorizontal: boolean } | null>(null);
const BOARD_ROWS = 8;
const BOARD_COLS = 9; // 8 + 1 extra square on top row
@ -70,6 +71,7 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
setDominoes(newDominoes);
setSelectedDomino(null);
setMoveCount(0);
setHoverPlacement(null);
}, []);
useEffect(() => {
@ -99,7 +101,7 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
};
// Check if we can place a domino at a position
const canPlaceDomino = (row: number, col: number, isHorizontal: boolean, excludeDominoId: number): boolean => {
const canPlaceDomino = useCallback((row: number, col: number, isHorizontal: boolean, excludeDominoId: number): boolean => {
const cell1Row = row;
const cell1Col = col;
const cell2Row = isHorizontal ? row : row + 1;
@ -109,35 +111,16 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
const occupied = getOccupiedCells(excludeDominoId);
return !occupied.has(`${cell1Row},${cell1Col}`) && !occupied.has(`${cell2Row},${cell2Col}`);
};
// Get valid placements for the selected domino
const getValidPlacements = useCallback((dominoId: number): { row: number; col: number; isHorizontal: boolean }[] => {
const placements: { row: number; col: number; isHorizontal: boolean }[] = [];
for (let row = 0; row < BOARD_ROWS; row++) {
const maxCol = row === 0 ? BOARD_COLS : 8;
for (let col = 0; col < maxCol; col++) {
// Check horizontal placement
if (canPlaceDomino(row, col, true, dominoId)) {
placements.push({ row, col, isHorizontal: true });
}
// Check vertical placement
if (canPlaceDomino(row, col, false, dominoId)) {
placements.push({ row, col, isHorizontal: false });
}
}
}
return placements;
}, [getOccupiedCells]);
// Handle clicking on a domino
const handleDominoClick = (dominoId: number) => {
if (selectedDomino === dominoId) {
setSelectedDomino(null);
setHoverPlacement(null);
} else {
setSelectedDomino(dominoId);
setHoverPlacement(null);
}
};
@ -151,15 +134,35 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
: d
));
setSelectedDomino(null);
setHoverPlacement(null);
setMoveCount(prev => prev + 1);
};
// Handle cell hover
const handleCellHover = (row: number, col: number) => {
if (selectedDomino === null) return;
// Check which placements are valid for this cell
const canH = canPlaceDomino(row, col, true, selectedDomino);
const canV = canPlaceDomino(row, col, false, selectedDomino);
if (canH && canV) {
// Prefer horizontal
setHoverPlacement({ row, col, isHorizontal: true });
} else if (canH) {
setHoverPlacement({ row, col, isHorizontal: true });
} else if (canV) {
setHoverPlacement({ row, col, isHorizontal: false });
} else {
setHoverPlacement(null);
}
};
// Check if puzzle is solved (all dominoes horizontal)
const isSolved = dominoes.length > 0 && dominoes.every(d => d.isHorizontal);
// Get domino color based on orientation
const getDominoColor = (isHorizontal: boolean, isSelected: boolean): string => {
if (isSelected) return 'bg-amber-400 border-amber-600';
const getDominoColor = (isHorizontal: boolean): string => {
if (isHorizontal) return 'bg-emerald-500 border-emerald-700';
return 'bg-rose-400 border-rose-600';
};
@ -167,20 +170,14 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
// Render the board
const renderBoard = () => {
const cells: JSX.Element[] = [];
const validPlacements = selectedDomino !== null ? getValidPlacements(selectedDomino) : [];
const occupied = getOccupiedCells();
const occupied = getOccupiedCells(selectedDomino ?? undefined);
// Render cells
for (let row = 0; row < BOARD_ROWS; row++) {
const maxCol = row === 0 ? BOARD_COLS : 8;
for (let col = 0; col < maxCol; col++) {
const isOccupied = occupied.has(`${row},${col}`);
const isExtraSquare = row === 0 && col === 8;
// Check if this is a valid horizontal placement start
const validHorizontal = validPlacements.find(p => p.row === row && p.col === col && p.isHorizontal);
const validVertical = validPlacements.find(p => p.row === row && p.col === col && !p.isHorizontal);
const cellStyle: React.CSSProperties = {
position: 'absolute',
left: col * 50,
@ -194,59 +191,31 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
key={`cell-${row}-${col}`}
className={`border border-border/50 ${
isExtraSquare ? 'bg-primary/20' : (row + col) % 2 === 0 ? 'bg-muted/30' : 'bg-muted/60'
}`}
} ${selectedDomino !== null ? 'cursor-pointer' : ''}`}
style={cellStyle}
/>
);
// Render valid placement indicators
if (validHorizontal && !isOccupied) {
cells.push(
<div
key={`valid-h-${row}-${col}`}
className="absolute bg-emerald-300/60 border-2 border-emerald-500 border-dashed rounded cursor-pointer hover:bg-emerald-400/70 transition-colors z-10"
style={{
left: col * 50 + 2,
top: row * 50 + 2,
width: 96,
height: 46,
}}
onClick={() => handlePlacementClick(row, col, true)}
title="Place horizontally"
/>
);
onMouseEnter={() => handleCellHover(row, col)}
onMouseLeave={() => setHoverPlacement(null)}
onClick={() => {
if (hoverPlacement && hoverPlacement.row === row && hoverPlacement.col === col) {
handlePlacementClick(hoverPlacement.row, hoverPlacement.col, hoverPlacement.isHorizontal);
}
if (validVertical && !isOccupied) {
cells.push(
<div
key={`valid-v-${row}-${col}`}
className="absolute bg-blue-300/60 border-2 border-blue-500 border-dashed rounded cursor-pointer hover:bg-blue-400/70 transition-colors z-10"
style={{
left: col * 50 + 2,
top: row * 50 + 2,
width: 46,
height: 96,
}}
onClick={() => handlePlacementClick(row, col, false)}
title="Place vertically"
/>
);
}
}
}
// Render dominoes
// Render dominoes (except selected one)
dominoes.forEach(d => {
const isSelected = selectedDomino === d.id;
if (d.id === selectedDomino) return; // Hide selected domino
const width = d.isHorizontal ? 96 : 46;
const height = d.isHorizontal ? 46 : 96;
cells.push(
<div
key={`domino-${d.id}`}
className={`absolute rounded border-2 cursor-pointer transition-all z-20 ${getDominoColor(d.isHorizontal, isSelected)} ${
isSelected ? 'ring-2 ring-amber-300 ring-offset-2 scale-105' : 'hover:brightness-110'
}`}
className={`absolute rounded border-2 cursor-pointer transition-all z-20 ${getDominoColor(d.isHorizontal)} hover:brightness-110`}
style={{
left: d.col * 50 + 2,
top: d.row * 50 + 2,
@ -256,12 +225,36 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
onClick={() => handleDominoClick(d.id)}
>
<div className="absolute inset-0 flex items-center justify-center">
<div className={`w-3 h-3 rounded-full ${isSelected ? 'bg-amber-700' : d.isHorizontal ? 'bg-emerald-700' : 'bg-rose-700'}`} />
<div className={`w-3 h-3 rounded-full ${d.isHorizontal ? 'bg-emerald-700' : 'bg-rose-700'}`} />
</div>
</div>
);
});
// Render hover preview domino
if (hoverPlacement && selectedDomino !== null) {
const width = hoverPlacement.isHorizontal ? 96 : 46;
const height = hoverPlacement.isHorizontal ? 46 : 96;
cells.push(
<div
key="hover-preview"
className="absolute rounded border-2 cursor-pointer z-30 bg-amber-400 border-amber-600 opacity-80 hover:opacity-100 transition-opacity"
style={{
left: hoverPlacement.col * 50 + 2,
top: hoverPlacement.row * 50 + 2,
width,
height,
pointerEvents: 'none',
}}
>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-3 h-3 rounded-full bg-amber-700" />
</div>
</div>
);
}
return cells;
};