File size: 4,996 Bytes
dadd5bd
 
 
044eef3
 
 
dadd5bd
 
 
044eef3
 
dadd5bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
044eef3
 
dadd5bd
044eef3
 
 
 
dadd5bd
ccf134f
dadd5bd
 
 
 
 
 
 
 
044eef3
dadd5bd
 
 
 
 
 
d06267b
dadd5bd
 
ccf134f
 
 
dadd5bd
 
 
d06267b
dadd5bd
d06267b
dadd5bd
 
044eef3
 
d06267b
dadd5bd
 
 
d06267b
dadd5bd
d06267b
dadd5bd
 
 
 
 
044eef3
 
 
 
dadd5bd
 
 
 
d06267b
dadd5bd
 
 
 
 
ccf134f
 
dadd5bd
044eef3
 
ccf134f
 
 
 
 
 
 
 
dadd5bd
 
044eef3
dadd5bd
044eef3
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { Download, Loader2 } from 'lucide-react';
import { useState, useRef, useEffect } from 'react';
import { LayoutType, CanvasSize } from '../../types/canvas.types';

interface ExportButtonProps {
  onExport: (filename: string) => void;
  isExporting?: boolean;
  currentLayout: LayoutType | null;
  canvasSize: CanvasSize;
}

export default function ExportButton({ onExport, isExporting = false, currentLayout, canvasSize }: ExportButtonProps) {
  // Map canvas size to friendly name
  const getCanvasSizeName = (size: CanvasSize): string => {
    switch (size) {
      case '1200x675':
        return 'Twitter';
      case 'linkedin':
        return 'LinkedIn';
      case 'hf':
        return 'HF';
      default:
        return 'Twitter';
    }
  };

  // Generate smart default filename
  const generateDefaultFilename = () => {
    const sizeName = getCanvasSizeName(canvasSize);
    if (currentLayout) {
      return `${currentLayout}_${sizeName}`;
    }
    return `thumbnail_${sizeName}`;
  };

  const [filename, setFilename] = useState(generateDefaultFilename());
  const [isFocused, setIsFocused] = useState(false);
  const [inputWidth, setInputWidth] = useState(0);
  const [isHoveringButton, setIsHoveringButton] = useState(false);
  const spanRef = useRef<HTMLSpanElement>(null);

  // Update filename when layout or canvas size changes
  useEffect(() => {
    setFilename(generateDefaultFilename());
  }, [currentLayout, canvasSize]);

  // Calculate input width based on text content
  useEffect(() => {
    if (spanRef.current) {
      setInputWidth(spanRef.current.offsetWidth);
    }
  }, [filename]);

  const handleExport = () => {
    if (isExporting) return;
    onExport(filename);
  };

  return (
    <div
      className="fixed top-[7px] lg:top-[10px] right-[20px] lg:right-[22px] z-50 p-[4px] lg:p-[5px] inline-flex items-stretch gap-[4px] lg:gap-[5px]"
      style={{
        backgroundColor: '#262933',
        borderRadius: '10px',
        boxShadow: '0px 0px 0px 0px rgba(0,0,0,0), 0px 0px 0px 0px rgba(0,0,0,0.03), 0px 0px 6.417px 0px rgba(0,0,0,0.09), 0px 0px 4.583px 0px rgba(0,0,0,0.15), 0px 0px 2.75px 0px rgba(0,0,0,0.17), 0px 16.847px 21.059px -4.212px rgba(14,13,13,0.1), 0px 8.423px 8.423px -4.212px rgba(0,0,0,0.04)',
        opacity: isExporting ? 0.5 : 1,
        transition: 'all 0.2s ease-in-out',
        width: 'fit-content'
      }}
    >
      {/* Download/Loading Icon with Blue Background - Clickable */}
      <button
        onClick={handleExport}
        disabled={isExporting}
        onMouseEnter={() => setIsHoveringButton(true)}
        onMouseLeave={() => setIsHoveringButton(false)}
        className="flex items-center justify-center rounded-[5px] w-[29px] lg:w-[32px] aspect-square flex-shrink-0 border-none p-0"
        style={{
          backgroundColor: isHoveringButton ? '#0d6ecc' : '#1888ff',
          cursor: isExporting ? 'not-allowed' : 'pointer',
          transition: 'all 0.2s ease-in-out',
          transform: isHoveringButton && !isExporting ? 'scale(1.05)' : 'scale(1)'
        }}
      >
        {isExporting ? (
          <Loader2 className="w-[14px] lg:w-4 h-[14px] lg:h-4" color="white" style={{ animation: 'spin 1s linear infinite' }} />
        ) : (
          <Download className="w-[14px] lg:w-4 h-[14px] lg:h-4" color="white" />
        )}
      </button>

      {/* Filename Container */}
      <div className="flex items-center gap-[2px] pr-[4px] lg:pr-[5px] min-h-[29px] lg:min-h-[32px]">
        {/* Hidden span to measure text width */}
        <span
          ref={spanRef}
          className="absolute invisible whitespace-pre text-[14px] lg:text-[16px] font-normal p-[4px] lg:p-[5px]"
          style={{
            fontFamily: 'Inter, sans-serif'
          }}
        >
          {filename || 'thumbnail_name'}
        </span>

        <input
          type="text"
          value={filename}
          onChange={(e) => setFilename(e.target.value)}
          onFocus={() => setIsFocused(true)}
          onBlur={() => setIsFocused(false)}
          disabled={isExporting}
          placeholder="thumbnail_name"
          className="bg-white/5 text-white text-[14px] lg:text-[16px] font-normal outline-none border-none p-[4px] lg:p-[5px] rounded"
          style={{
            fontFamily: 'Inter, sans-serif',
            width: `${inputWidth}px`,
            cursor: isExporting ? 'not-allowed' : 'text',
            opacity: isFocused ? 1 : 0.5,
            transition: 'all 0.2s ease-in-out',
            backgroundColor: isFocused ? 'rgba(255, 255, 255, 0.1)' : 'rgba(255, 255, 255, 0.05)'
          }}
          onClick={(e) => e.stopPropagation()}
        />
        <span
          className="text-white text-[14px] lg:text-[16px] font-normal"
          style={{
            fontFamily: 'Inter, sans-serif',
            opacity: isFocused ? 1 : 0.5,
            transition: 'opacity 0.2s ease-in-out'
          }}
        >
          .png
        </span>
      </div>
    </div>
  );
}