205 lines
65beb53 samthecodingguy Jan 29, 2026
'use client';
import React, { useRef } from 'react';
import { Group, Rect, Circle } from 'react-konva';
import Konva from 'konva';
import type {
  Layer,
  EffectLayerData,
  GlassEffectConfig,
  BlurEffectConfig,
  GlowEffectConfig,
  ShadowEffectConfig,
} from '@/types';
interface EffectLayerProps {
  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 EffectLayer({
  id,
  layer,
  isSelected,
  onClick,
  onTransformEnd,
  onDragStart,
  onDragMove,
  onDragEnd,
}: EffectLayerProps) {
  const groupRef = useRef<Konva.Group>(null);
  const data = layer.data as EffectLayerData;
  const renderEffect = () => {
    switch (data.effectType) {
      case 'glass':
        return <GlassEffect layer={layer} config={data.config as GlassEffectConfig} />;
      case 'blur':
        return <BlurEffect layer={layer} config={data.config as BlurEffectConfig} />;
      case 'glow':
        return <GlowEffect layer={layer} config={data.config as GlowEffectConfig} />;
      case 'shadow':
        return <ShadowEffect layer={layer} config={data.config as ShadowEffectConfig} />;
      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)}
    >
      {renderEffect()}
    </Group>
  );
}
// Glass Effect Component
function GlassEffect({ layer, config }: { layer: Layer; config: GlassEffectConfig }) {
  // Konva doesn't support backdrop-filter, so we simulate the glass effect
  // with semi-transparent overlays and gradients
  return (
    <Group>
      {/* Base glass panel */}
      <Rect
        width={layer.size.width}
        height={layer.size.height}
        fill={`rgba(255, 255, 255, ${config.opacity})`}
        cornerRadius={config.borderRadius}
        stroke={config.borderColor}
        strokeWidth={config.borderWidth}
        shadowColor="rgba(0, 0, 0, 0.1)"
        shadowBlur={20}
        shadowOffsetY={10}
      />
      {/* Inner glow / highlight */}
      <Rect
        x={config.borderWidth}
        y={config.borderWidth}
        width={layer.size.width - config.borderWidth * 2}
        height={layer.size.height - config.borderWidth * 2}
        fillLinearGradientStartPoint={{ x: 0, y: 0 }}
        fillLinearGradientEndPoint={{ x: 0, y: layer.size.height * 0.3 }}
        fillLinearGradientColorStops={[
          0,
          'rgba(255, 255, 255, 0.2)',
          1,
          'rgba(255, 255, 255, 0)',
        ]}
        cornerRadius={config.borderRadius - config.borderWidth}
        listening={false}
      />
      {/* Subtle border highlight */}
      <Rect
        x={1}
        y={1}
        width={layer.size.width - 2}
        height={layer.size.height - 2}
        stroke="rgba(255, 255, 255, 0.1)"
        strokeWidth={1}
        cornerRadius={config.borderRadius - 1}
        listening={false}
      />
    </Group>
  );
}
// Blur Effect Component (Spotlight/Focus area)
function BlurEffect({ layer, config }: { layer: Layer; config: BlurEffectConfig }) {
  if (config.shape === '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}
        fill="rgba(0, 0, 0, 0.5)"
        filters={[Konva.Filters.Blur]}
        blurRadius={config.blur}
      />
    );
  }
  return (
    <Rect
      width={layer.size.width}
      height={layer.size.height}
      fill="rgba(0, 0, 0, 0.5)"
      filters={[Konva.Filters.Blur]}
      blurRadius={config.blur}
    />
  );
}
// Glow Effect Component
function GlowEffect({ layer, config }: { layer: Layer; config: GlowEffectConfig }) {
  const centerX = layer.size.width / 2;
  const centerY = layer.size.height / 2;
  const maxRadius = Math.max(layer.size.width, layer.size.height) / 2;
  return (
    <Group>
      {/* Outer glow */}
      <Circle
        x={centerX}
        y={centerY}
        radius={maxRadius}
        fillRadialGradientStartPoint={{ x: 0, y: 0 }}
        fillRadialGradientEndPoint={{ x: 0, y: 0 }}
        fillRadialGradientStartRadius={0}
        fillRadialGradientEndRadius={maxRadius}
        fillRadialGradientColorStops={[
          0,
          config.color,
          0.5,
          config.color.replace(')', ', 0.5)').replace('rgb', 'rgba'),
          1,
          'transparent',
        ]}
        shadowColor={config.color}
        shadowBlur={config.blur}
        shadowOffset={{ x: 0, y: 0 }}
      />
    </Group>
  );
}
// Shadow Effect Component
function ShadowEffect({ layer, config }: { layer: Layer; config: ShadowEffectConfig }) {
  return (
    <Rect
      width={layer.size.width}
      height={layer.size.height}
      fill="transparent"
      shadowColor={config.color}
      shadowBlur={config.blur}
      shadowOffsetX={config.offsetX}
      shadowOffsetY={config.offsetY}
    />
  );
}