162 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React, { useState } from 'react';
 | |
| import Image from 'next/image';
 | |
| import { ColorScheme } from '../utils/colorSchemes';
 | |
| import { generateYAML } from '../utils/yamlExport';
 | |
| import { Highlight, themes } from 'prism-react-renderer';
 | |
| import { motion, useAnimation } from 'framer-motion';
 | |
| 
 | |
| interface ColorSchemeCardProps {
 | |
|   scheme: ColorScheme;
 | |
|   onLike: () => void;
 | |
|   onDislike: () => void;
 | |
|   index: number;
 | |
|   isDarkMode: boolean;
 | |
| }
 | |
| 
 | |
| const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, onLike, onDislike, index, isDarkMode }) => {
 | |
|   const [overlayColor, setOverlayColor] = useState('rgba(0, 0, 0, 0)');
 | |
|   const controls = useAnimation();
 | |
| 
 | |
|   const codeExample = `
 | |
| // User object and function
 | |
| const user = {
 | |
|     name: 'DWS',
 | |
|     power: 8999
 | |
| };
 | |
| 
 | |
| class Shape {
 | |
|     constructor(color) {
 | |
|         this.color = color;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Async data fetch simulation
 | |
| async function fetchData() {
 | |
|     return await new Promise(resolve => setTimeout(() => resolve('Data loaded'), 500));
 | |
| }
 | |
| const [even, odd] = [2, 4, 6, 8].reduce(([e, o], n) => n % 2 ? [e, [...o, n]] : [
 | |
|     [...e, n], o
 | |
| ], [
 | |
|     [],
 | |
|     []
 | |
| ]);
 | |
| 
 | |
| /*
 | |
| Logging here
 | |
| */
 | |
| fetchData().then(data => console.log(data)); `;
 | |
| 
 | |
|   const handleDownload = (e: React.MouseEvent) => {
 | |
|     e.stopPropagation();
 | |
|     const yaml = generateYAML(scheme);
 | |
|     const blob = new Blob([yaml], { type: 'text/yaml' });
 | |
|     const url = URL.createObjectURL(blob);
 | |
|     const a = document.createElement('a');
 | |
|     a.href = url;
 | |
|     a.download = `${scheme.name.replace(/\s+/g, '_').toLowerCase()}.yaml`;
 | |
|     document.body.appendChild(a);
 | |
|     a.click();
 | |
|     document.body.removeChild(a);
 | |
|     URL.revokeObjectURL(url);
 | |
|   };
 | |
| 
 | |
|   const handleLike = () => {
 | |
|     setOverlayColor('rgba(0, 255, 0, 0.1)');
 | |
|     controls.start({ x: 300, opacity: 0, transition: { duration: 0.3 } }).then(onLike);
 | |
|   };
 | |
| 
 | |
|   const handleDislike = () => {
 | |
|     setOverlayColor('rgba(255, 0, 0, 0.1)');
 | |
|     controls.start({ x: -300, opacity: 0, transition: { duration: 0.3 } }).then(onDislike);
 | |
|   };
 | |
| 
 | |
|   return (
 | |
|     <motion.div
 | |
|       className="absolute w-[350px] h-[600px] bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
 | |
|       initial={{ scale: 1 - index * 0.05, y: index * 15, opacity: 1 }}
 | |
|       animate={controls}
 | |
|       style={{ 
 | |
|         zIndex: 3 - index
 | |
|       }}
 | |
|       drag="x"
 | |
|       dragConstraints={{ left: -100, right: 100 }}
 | |
|       onDragEnd={(e, { offset, velocity }) => {
 | |
|         if (offset.x > 100) handleLike();
 | |
|         else if (offset.x < -100) handleDislike();
 | |
|       }}
 | |
|     >
 | |
|       <div className="p-6 h-full flex flex-col relative">
 | |
|         <motion.div 
 | |
|           className="absolute inset-0 rounded-lg"
 | |
|           animate={{ backgroundColor: overlayColor }}
 | |
|           initial={{ backgroundColor: 'rgba(0, 0, 0, 0)' }}
 | |
|           transition={{ duration: 0.2 }}
 | |
|         />
 | |
|         <div className="flex justify-between items-center mb-4 z-10">
 | |
|           <h2 className="text-xl font-semibold">{scheme.name}</h2>
 | |
|           <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={24} height={24} />
 | |
|           </button>
 | |
|         </div>
 | |
|         <div className="bg-gray-100 dark:bg-gray-700 rounded-md mb-4 flex-grow overflow-hidden z-10 shadow-md">
 | |
|           <Highlight theme={themes.dracula} code={codeExample} language="javascript">
 | |
|             {({ className, style, tokens, getLineProps, getTokenProps }) => (
 | |
|               <pre className={`${className} text-xs p-4 overflow-x-auto`} style={{ ...style, backgroundColor: scheme.colors.primary.background }}>
 | |
|                 {tokens.slice(0, 20).map((line, i) => (
 | |
|                   <div key={i} {...getLineProps({ line, key: i })}>
 | |
|                     {line.map((token, key) => {
 | |
|                       let color = scheme.colors.primary.foreground;
 | |
|                       if (token.types.includes('keyword')) color = scheme.colors.normal.blue;
 | |
|                       if (token.types.includes('string')) color = scheme.colors.normal.green;
 | |
|                       if (token.types.includes('number')) color = scheme.colors.normal.magenta;
 | |
|                       if (token.types.includes('comment')) color = scheme.colors.bright.black;
 | |
|                       if (token.types.includes('function')) color = scheme.colors.normal.yellow;
 | |
|                       if (token.types.includes('operator')) color = scheme.colors.normal.cyan;
 | |
|                       if (token.types.includes('class-name')) color = scheme.colors.bright.magenta;
 | |
|                       if (token.types.includes('constant')) color = scheme.colors.bright.red;
 | |
|                       if (token.types.includes('punctuation')) color = scheme.colors.bright.cyan;
 | |
|                       if (token.types.includes('variable')) color = scheme.colors.bright.yellow;
 | |
|                       return <span key={key} {...getTokenProps({ token, key })} style={{ ...getTokenProps({ token, key }).style, color }} />;
 | |
|                     })}
 | |
|                   </div>
 | |
|                 ))}
 | |
|               </pre>
 | |
|             )}
 | |
|           </Highlight>
 | |
|         </div>
 | |
|         <div className="grid grid-cols-8 gap-2 mb-4 z-10">
 | |
|           {Object.values(scheme.colors.normal).concat(Object.values(scheme.colors.bright)).map((color, index) => (
 | |
|             <div 
 | |
|               key={index} 
 | |
|               className="w-full pt-full rounded-sm transition-colors duration-300 relative group" 
 | |
|               style={{backgroundColor: color}}
 | |
|             >
 | |
|               <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-black bg-opacity-50 text-white text-[8px]">
 | |
|                 {color}
 | |
|               </div>
 | |
|             </div>
 | |
|           ))}
 | |
|         </div>
 | |
|         <div className="flex justify-center space-x-8 mt-4 z-10">
 | |
|           <button 
 | |
|             className="bg-red-500 text-white p-3 rounded-full shadow-lg hover:bg-red-600 transition-colors duration-300"
 | |
|             onClick={handleDislike}
 | |
|           >
 | |
|             <Image src={isDarkMode ? "/cross-icon-dark.svg" : "/cross-icon-light.svg"} alt="Dislike" width={28} height={28} />
 | |
|           </button>
 | |
|           <button 
 | |
|             className="bg-green-500 text-white p-3 rounded-full shadow-lg hover:bg-green-600 transition-colors duration-300"
 | |
|             onClick={handleLike}
 | |
|           >
 | |
|             <Image src={isDarkMode ? "/heart-icon-dark.svg" : "/heart-icon-light.svg"} alt="Like" width={28} height={28} />
 | |
|           </button>
 | |
|         </div>
 | |
|       </div>
 | |
|     </motion.div>
 | |
|   );
 | |
| };
 | |
| 
 | |
| export default ColorSchemeCard; |