ChunDe's picture
refactor: Update text positioning and rebuild production assets
ccf134f
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>
);
}