TerminalTinder/app/components/ColorSchemeCard.tsx

182 lines
6.3 KiB
TypeScript
Raw Normal View History

2024-09-09 13:31:32 -04:00
import React, { useState } from 'react';
2024-09-09 11:51:54 -04:00
import Image from 'next/image';
import { ColorScheme, encodeThemeForUrl } from '../utils/colorSchemes';
import { generateYAML, generateJSON, generateXResources, generateTOML, generateITerm2, generateWindowsTerminal, generateTerminalApp } from '../utils/exportFormats';
2024-09-09 13:31:32 -04:00
import { motion, useAnimation } from 'framer-motion';
import ColorPalette from './ColorPalette';
import confetti from 'canvas-confetti';
import { AppSettings } from '../utils/types';
import CodePreview from './CodePreview';
2024-09-09 11:51:54 -04:00
interface ColorSchemeCardProps {
scheme: ColorScheme;
2024-09-09 13:31:32 -04:00
onLike: () => void;
onDislike: () => void;
index: number;
2024-09-09 15:31:19 -04:00
isDarkMode: boolean;
settings: AppSettings;
2024-09-09 11:51:54 -04:00
}
const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, onLike, onDislike, index, isDarkMode, settings }) => {
2024-09-09 13:31:32 -04:00
const [overlayColor, setOverlayColor] = useState('rgba(0, 0, 0, 0)');
const controls = useAnimation();
2024-09-09 11:51:54 -04:00
const handleDownload = (e: React.MouseEvent) => {
e.stopPropagation();
2024-09-09 16:59:00 -04:00
let content: string;
let fileExtension: string;
switch (settings.outputFormat) {
2024-09-09 16:59:00 -04:00
case 'json':
content = generateJSON(scheme);
fileExtension = 'json';
break;
case 'xresources':
content = generateXResources(scheme);
fileExtension = 'Xresources';
break;
case 'toml':
content = generateTOML(scheme);
fileExtension = 'toml';
break;
case 'iterm2':
content = generateITerm2(scheme);
fileExtension = 'itermcolors';
break;
case 'windows-terminal':
content = generateWindowsTerminal(scheme);
fileExtension = 'json';
break;
case 'terminal-app':
content = generateTerminalApp(scheme);
fileExtension = 'terminal';
break;
2024-09-09 16:59:00 -04:00
case 'yaml':
default:
content = generateYAML(scheme);
fileExtension = 'yaml';
}
const blob = new Blob([content], { type: 'text/plain' });
2024-09-09 11:51:54 -04:00
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
2024-09-09 16:59:00 -04:00
a.download = `${scheme.name.replace(/\s+/g, '_').toLowerCase()}.${fileExtension}`;
2024-09-09 11:51:54 -04:00
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
2024-09-09 13:31:32 -04:00
const handleLike = () => {
setOverlayColor('rgba(0, 255, 0, 0.1)');
controls.start({ x: 300, opacity: 0, transition: { duration: 0.3 } }).then(() => {
if (settings.partyMode) {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 }
});
}
onLike();
});
2024-09-09 13:31:32 -04:00
};
const handleDislike = () => {
setOverlayColor('rgba(255, 0, 0, 0.1)');
controls.start({ x: -300, opacity: 0, transition: { duration: 0.3 } }).then(onDislike);
};
const handleShare = (e: React.MouseEvent) => {
e.stopPropagation();
const encodedTheme = encodeThemeForUrl(scheme);
window.open(`/share/${encodedTheme}`, '_blank');
};
2024-09-09 16:59:00 -04:00
const paletteColors = [
scheme.colors.normal.black,
scheme.colors.normal.red,
scheme.colors.normal.green,
scheme.colors.normal.yellow,
scheme.colors.normal.blue,
scheme.colors.normal.magenta,
scheme.colors.normal.cyan,
scheme.colors.normal.white,
scheme.colors.bright.black,
scheme.colors.bright.red,
scheme.colors.bright.green,
scheme.colors.bright.yellow,
scheme.colors.bright.blue,
scheme.colors.bright.magenta,
scheme.colors.bright.cyan,
scheme.colors.bright.white,
];
2024-09-09 11:51:54 -04:00
return (
2024-09-09 13:31:32 -04:00
<motion.div
2024-09-09 16:59:00 -04:00
className="absolute w-[80vw] max-w-[480px] h-[90vh] max-h-[600px] bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
2024-09-09 13:31:32 -04:00
initial={{ scale: 1 - index * 0.05, y: index * 15, opacity: 1 }}
animate={controls}
style={{
zIndex: 3 - index
}}
drag="x"
2024-09-09 16:59:00 -04:00
dragConstraints={{ left: -50, right: 50 }}
2024-09-09 18:08:56 -04:00
onDragEnd={(e, { offset }) => {
2024-09-09 16:59:00 -04:00
if (offset.x > 50) handleLike();
else if (offset.x < -50) handleDislike();
2024-09-09 13:31:32 -04:00
}}
2024-09-09 11:51:54 -04:00
>
2024-09-09 16:59:00 -04:00
<div className="p-4 h-full flex flex-col relative">
2024-09-09 13:31:32 -04:00
<motion.div
className="absolute inset-0 rounded-lg"
animate={{ backgroundColor: overlayColor }}
initial={{ backgroundColor: 'rgba(0, 0, 0, 0)' }}
transition={{ duration: 0.2 }}
/>
2024-09-09 16:59:00 -04:00
<div className="flex justify-between items-center mb-2 z-10">
<h2 className="text-lg font-semibold truncate">{scheme.name}</h2>
<div className="flex space-x-2">
<button
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors duration-300"
onClick={handleShare}
>
<Image src={isDarkMode ? "/share-icon-dark.svg" : "/share-icon-light.svg"} alt="Share" width={20} height={20} />
</button>
<button
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors duration-300"
onClick={handleDownload}
>
<Image src={isDarkMode ? "/download-icon-dark.svg" : "/download-icon-light.svg"} alt="Download" width={20} height={20} />
</button>
</div>
2024-09-09 11:51:54 -04:00
</div>
2024-09-09 16:59:00 -04:00
<div className="bg-gray-100 dark:bg-gray-700 rounded-md mb-2 flex-grow overflow-hidden z-10 shadow-md">
<CodePreview scheme={scheme} codeSample={settings.codeSample} />
2024-09-09 11:51:54 -04:00
</div>
2024-09-09 16:59:00 -04:00
<div className="mt-2 z-10">
<ColorPalette
colors={paletteColors}
size="small"
/>
</div>
<div className="flex justify-center space-x-6 mt-4 z-10">
2024-09-09 13:31:32 -04:00
<button
className="bg-red-500 text-white p-3 rounded-full shadow-lg hover:bg-red-600 transition-colors duration-300"
2024-09-09 13:31:32 -04:00
onClick={handleDislike}
>
<Image src={isDarkMode ? "/cross-icon-dark.svg" : "/cross-icon-light.svg"} alt="Dislike" width={32} height={32} />
2024-09-09 13:31:32 -04:00
</button>
<button
className="bg-green-500 text-white p-3 rounded-full shadow-lg hover:bg-green-600 transition-colors duration-300"
2024-09-09 13:31:32 -04:00
onClick={handleLike}
>
<Image src={isDarkMode ? "/heart-icon-dark.svg" : "/heart-icon-light.svg"} alt="Like" width={32} height={32} />
2024-09-09 13:31:32 -04:00
</button>
</div>
2024-09-09 11:51:54 -04:00
</div>
2024-09-09 13:31:32 -04:00
</motion.div>
2024-09-09 11:51:54 -04:00
);
};
export default ColorSchemeCard;