| Server IP : 217.154.3.148 / Your IP : 216.73.216.90 Web Server : Apache System : Linux blissful-wright.217-154-3-148.plesk.page 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 User : gracious-boyd_hfhh1h8vo9n ( 10000) PHP Version : 8.3.31 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/src/components/ |
Upload File : |
'use client';
import React, { useMemo } from 'react';
import type { TileScene, TileAssignment, TileDesign } from '@/src/types/tiles';
import { getTileVariation, tileKey } from '@/src/utils/tiles';
type ScenePreviewProps = {
scene: TileScene;
assignments: TileAssignment[];
designsById: Record<number, TileDesign>;
selectedTiles: Set<string>;
onToggleTile: (row: number, col: number) => void;
};
export default function ScenePreview({
scene,
assignments,
designsById,
selectedTiles,
onToggleTile,
}: ScenePreviewProps) {
const overlayWidth = scene.tileWidthPx * scene.cols;
const overlayHeight = scene.tileHeightPx * scene.rows;
const groutColor = scene.groutColor ?? 'rgba(255,255,255,0.75)';
const sortedAssignments = useMemo(
() => [...assignments].sort((a, b) => (a.row === b.row ? a.col - b.col : a.row - b.row)),
[assignments],
);
return (
<div
style={{
position: 'relative',
width: '100%',
borderRadius: 12,
overflow: 'hidden',
border: '1px solid #e5e7eb',
backgroundColor: '#111827',
}}
>
<img
src={scene.imageUrl}
alt={scene.label}
style={{ width: '100%', display: 'block', objectFit: 'cover' }}
/>
<div
style={{
position: 'absolute',
inset: 0,
background: 'radial-gradient(circle at center, transparent 40%, rgba(0,0,0,0.18))',
pointerEvents: 'none',
}}
/>
<div
style={{
position: 'absolute',
left: scene.offsetX ?? 0,
top: scene.offsetY ?? 0,
width: overlayWidth,
height: overlayHeight,
display: 'grid',
gridTemplateColumns: `repeat(${scene.cols}, ${scene.tileWidthPx}px)`,
gridTemplateRows: `repeat(${scene.rows}, ${scene.tileHeightPx}px)`,
boxShadow: '0 16px 30px rgba(0,0,0,0.22)',
pointerEvents: 'none',
transform: scene.perspectiveTransform ?? 'none',
transformOrigin: 'center center',
filter: scene.overlayFilter ?? 'none',
overflow: 'visible',
}}
>
{sortedAssignments.map((tile) => {
const design = tile.designId ? designsById[tile.designId] : null;
const key = tileKey(tile.row, tile.col);
const isSelected = selectedTiles.has(key);
const { rotateDeg, scale } = getTileVariation(tile.row, tile.col);
return (
<button
key={`${tile.row}-${tile.col}`}
type="button"
onClick={(e) => {
e.stopPropagation();
onToggleTile(tile.row, tile.col);
}}
style={{
backgroundImage: design ? `url(${design.imageUrl})` : 'none',
backgroundSize: 'cover',
backgroundPosition: 'center',
border: `1px solid ${groutColor}`,
cursor: 'pointer',
boxShadow: `${isSelected ? '0 0 0 2px #2563eb inset, ' : ''} inset 0 0 4px rgba(0,0,0,0.12)`,
outline: isSelected ? '1px solid rgba(37,99,235,0.8)' : undefined,
backgroundColor: isSelected ? 'rgba(255,255,255,0.08)' : undefined,
pointerEvents: 'auto',
position: 'relative',
boxSizing: 'border-box',
transform: `scale(${scale}) rotate(${rotateDeg}deg)`,
transformOrigin: 'center center',
}}
>
{isSelected ? (
<div
style={{
position: 'absolute',
inset: 0,
background: 'radial-gradient(circle at center, rgba(37,99,235,0.18), transparent 60%)',
pointerEvents: 'none',
}}
/>
) : null}
</button>
);
})}
<div
style={{
position: 'absolute',
inset: 0,
pointerEvents: 'none',
background: 'linear-gradient(to bottom, rgba(255,255,255,0.18), rgba(0,0,0,0.12))',
mixBlendMode: 'soft-light',
}}
/>
</div>
</div>
);
}