166 lines
65beb53 samthecodingguy Jan 29, 2026
'use client';
import React, { useRef } from 'react';
import { Group, Rect, Circle, Ellipse, RegularPolygon, Star, Arrow, Line } from 'react-konva';
import type Konva from 'konva';
import type { Layer, ShapeLayerData, GradientConfig } from '@/types';
interface ShapeLayerProps {
  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 ShapeLayer({
  id,
  layer,
  isSelected,
  onClick,
  onTransformEnd,
  onDragStart,
  onDragMove,
  onDragEnd,
}: ShapeLayerProps) {
  const groupRef = useRef<Konva.Group>(null);
  const data = layer.data as ShapeLayerData;
  // Build fill props based on whether it's solid or gradient
  const getFillProps = () => {
    if (typeof data.fill === 'string') {
      return { fill: data.fill };
    }
    const gradient = data.fill as GradientConfig;
    if (gradient.type === 'linear') {
      const angle = gradient.angle || 0;
      const rad = (angle * Math.PI) / 180;
      return {
        fillLinearGradientStartPoint: {
          x: layer.size.width / 2 - (Math.cos(rad) * layer.size.width) / 2,
          y: layer.size.height / 2 - (Math.sin(rad) * layer.size.height) / 2,
        },
        fillLinearGradientEndPoint: {
          x: layer.size.width / 2 + (Math.cos(rad) * layer.size.width) / 2,
          y: layer.size.height / 2 + (Math.sin(rad) * layer.size.height) / 2,
        },
        fillLinearGradientColorStops: gradient.stops.flatMap((s) => [s.offset, s.color]),
      };
    }
    return {
      fillRadialGradientStartPoint: { x: layer.size.width / 2, y: layer.size.height / 2 },
      fillRadialGradientEndPoint: { x: layer.size.width / 2, y: layer.size.height / 2 },
      fillRadialGradientStartRadius: 0,
      fillRadialGradientEndRadius: Math.max(layer.size.width, layer.size.height) / 2,
      fillRadialGradientColorStops: gradient.stops.flatMap((s) => [s.offset, s.color]),
    };
  };
  const commonProps = {
    stroke: data.stroke,
    strokeWidth: data.strokeWidth,
    ...getFillProps(),
  };
  const renderShape = () => {
    switch (data.shapeType) {
      case 'rectangle':
        return (
          <Rect
            width={layer.size.width}
            height={layer.size.height}
            cornerRadius={data.cornerRadius || 0}
            {...commonProps}
          />
        );
      case 'circle':
        const radius = Math.min(layer.size.width, layer.size.height) / 2;
        return (
          <Circle
            x={layer.size.width / 2}
            y={layer.size.height / 2}
            radius={radius}
            {...commonProps}
          />
        );
      case 'ellipse':
        return (
          <Ellipse
            x={layer.size.width / 2}
            y={layer.size.height / 2}
            radiusX={layer.size.width / 2}
            radiusY={layer.size.height / 2}
            {...commonProps}
          />
        );
      case 'polygon':
        const polyRadius = Math.min(layer.size.width, layer.size.height) / 2;
        return (
          <RegularPolygon
            x={layer.size.width / 2}
            y={layer.size.height / 2}
            sides={data.points || 6}
            radius={polyRadius}
            {...commonProps}
          />
        );
      case 'star':
        const starRadius = Math.min(layer.size.width, layer.size.height) / 2;
        return (
          <Star
            x={layer.size.width / 2}
            y={layer.size.height / 2}
            numPoints={data.points || 5}
            innerRadius={starRadius * 0.5}
            outerRadius={starRadius}
            {...commonProps}
          />
        );
      case 'arrow':
        return (
          <Arrow
            points={[0, layer.size.height / 2, layer.size.width, layer.size.height / 2]}
            pointerLength={20}
            pointerWidth={20}
            {...commonProps}
          />
        );
      default:
        return null;
    }
  };
  return (
    <Group
      ref={groupRef}
      id={id}
      x={layer.position.x}
      y={layer.position.y}
      width={layer.size.width}
      height={layer.size.height}
      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)}
    >
      {renderShape()}
    </Group>
  );
}