229 lines
65beb53 samthecodingguy Jan 29, 2026
'use client';
import React, { useEffect, useRef } from 'react';
import { Group, Rect, Image as KonvaImage } from 'react-konva';
import useImage from 'use-image';
import type Konva from 'konva';
import type { Layer, DeviceLayerData, DeviceColor } from '@/types';
import { DEVICE_SPECS, DEVICE_COLORS } from '@/lib/devices/device-specs';
interface DeviceLayerProps {
  id: string;
  layer: Layer;
  isSelected: boolean;
  onClick: (e: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => void;
  onTransformEnd: (node: Konva.Node) => void;
  onDragStart?: () => void;
  onDragMove?: (node: Konva.Node) => void;
  onDragEnd: (node: Konva.Node) => void;
}
export function DeviceLayer({
  id,
  layer,
  isSelected,
  onClick,
  onTransformEnd,
  onDragStart,
  onDragMove,
  onDragEnd,
}: DeviceLayerProps) {
  const groupRef = useRef<Konva.Group>(null);
  const data = layer.data as DeviceLayerData;
  const spec = DEVICE_SPECS[data.deviceId];
  if (!spec) return null;
  const frameColor = DEVICE_COLORS[data.color] || DEVICE_COLORS.black;
  // Calculate scale to fit layer size
  const scaleX = layer.size.width / spec.frameSize.width;
  const scaleY = layer.size.height / spec.frameSize.height;
  const scale = Math.min(scaleX, scaleY);
  const scaledFrameWidth = spec.frameSize.width * scale;
  const scaledFrameHeight = spec.frameSize.height * scale;
  const scaledScreenWidth = spec.screenSize.width * scale;
  const scaledScreenHeight = spec.screenSize.height * scale;
  const scaledOffsetX = spec.screenOffset.x * scale;
  const scaledOffsetY = spec.screenOffset.y * scale;
  return (
    <Group
      ref={groupRef}
      id={id}
      x={layer.position.x}
      y={layer.position.y}
      rotation={layer.rotation}
      opacity={layer.opacity}
      draggable={!layer.locked}
      onClick={onClick}
      onTap={onClick}
      onDragStart={onDragStart}
      onDragMove={(e) => onDragMove?.(e.target)}
      onDragEnd={(e) => onDragEnd(e.target)}
      onTransformEnd={(e) => onTransformEnd(e.target)}
    >
      {/* Device Frame */}
      {data.showFrame && (
        <>
          {/* Outer frame with shadow */}
          <Rect
            x={0}
            y={0}
            width={scaledFrameWidth}
            height={scaledFrameHeight}
            fill={frameColor}
            cornerRadius={spec.cornerRadius * scale}
            shadowColor="rgba(0, 0, 0, 0.4)"
            shadowBlur={20 * scale}
            shadowOffsetY={10 * scale}
          />
          {/* Inner bezel highlight */}
          <Rect
            x={2 * scale}
            y={2 * scale}
            width={scaledFrameWidth - 4 * scale}
            height={scaledFrameHeight - 4 * scale}
            stroke="rgba(255, 255, 255, 0.1)"
            strokeWidth={1}
            cornerRadius={(spec.cornerRadius - 2) * scale}
            listening={false}
          />
          {/* Side buttons for iPhone */}
          {spec.category === 'iphone' && (
            <>
              {/* Volume buttons */}
              <Rect
                x={-3 * scale}
                y={120 * scale}
                width={3 * scale}
                height={30 * scale}
                fill={frameColor}
                cornerRadius={2 * scale}
              />
              <Rect
                x={-3 * scale}
                y={160 * scale}
                width={3 * scale}
                height={60 * scale}
                fill={frameColor}
                cornerRadius={2 * scale}
              />
              {/* Power button */}
              <Rect
                x={scaledFrameWidth}
                y={140 * scale}
                width={3 * scale}
                height={70 * scale}
                fill={frameColor}
                cornerRadius={2 * scale}
              />
            </>
          )}
          {/* Dynamic Island / Notch */}
          {spec.category === 'iphone' && spec.id !== 'iphone-se' && (
            <Rect
              x={scaledFrameWidth / 2 - 60 * scale}
              y={12 * scale}
              width={120 * scale}
              height={35 * scale}
              fill="#000"
              cornerRadius={18 * scale}
            />
          )}
        </>
      )}
      {/* Screen area */}
      <Rect
        x={data.showFrame ? scaledOffsetX : 0}
        y={data.showFrame ? scaledOffsetY : 0}
        width={data.showFrame ? scaledScreenWidth : layer.size.width}
        height={data.showFrame ? scaledScreenHeight : layer.size.height}
        fill="#000"
        cornerRadius={data.showFrame ? (spec.cornerRadius - 5) * scale : 0}
      />
      {/* Screenshot placeholder */}
      {!data.screenshotId && (
        <Group
          x={data.showFrame ? scaledOffsetX : 0}
          y={data.showFrame ? scaledOffsetY : 0}
        >
          <Rect
            width={data.showFrame ? scaledScreenWidth : layer.size.width}
            height={data.showFrame ? scaledScreenHeight : layer.size.height}
            fill="rgba(255, 255, 255, 0.05)"
            cornerRadius={data.showFrame ? (spec.cornerRadius - 5) * scale : 0}
          />
          {/* Placeholder icon */}
          <Rect
            x={(data.showFrame ? scaledScreenWidth : layer.size.width) / 2 - 30 * scale}
            y={(data.showFrame ? scaledScreenHeight : layer.size.height) / 2 - 30 * scale}
            width={60 * scale}
            height={60 * scale}
            fill="rgba(255, 255, 255, 0.1)"
            cornerRadius={10 * scale}
          />
        </Group>
      )}
      {/* Screen reflection overlay */}
      {data.showFrame && (
        <Rect
          x={scaledOffsetX}
          y={scaledOffsetY}
          width={scaledScreenWidth}
          height={scaledScreenHeight / 3}
          fillLinearGradientStartPoint={{ x: 0, y: 0 }}
          fillLinearGradientEndPoint={{ x: 0, y: scaledScreenHeight / 3 }}
          fillLinearGradientColorStops={[
            0,
            'rgba(255, 255, 255, 0.1)',
            1,
            'rgba(255, 255, 255, 0)',
          ]}
          cornerRadius={[(spec.cornerRadius - 5) * scale, (spec.cornerRadius - 5) * scale, 0, 0]}
          listening={false}
        />
      )}
    </Group>
  );
}
// Screenshot component for device
interface DeviceScreenshotProps {
  src: string;
  width: number;
  height: number;
  cornerRadius: number;
}
export function DeviceScreenshot({ src, width, height, cornerRadius }: DeviceScreenshotProps) {
  const [image] = useImage(src, 'anonymous');
  if (!image) return null;
  return (
    <Group
      clipFunc={(ctx) => {
        ctx.beginPath();
        ctx.roundRect(0, 0, width, height, cornerRadius);
        ctx.closePath();
      }}
    >
      <KonvaImage
        image={image}
        width={width}
        height={height}
        listening={false}
      />
    </Group>
  );
}