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:
commit
5671af101c
1 changed files with 66 additions and 73 deletions
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue