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>
);
}
|