History, more icons, color palette component
This commit is contained in:
79
app/components/ColorPalette.tsx
Normal file
79
app/components/ColorPalette.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
|
||||
interface ColorPaletteProps {
|
||||
colors: string[];
|
||||
size?: 'small' | 'large';
|
||||
}
|
||||
|
||||
const ColorPalette: React.FC<ColorPaletteProps> = ({ colors, size = 'small' }) => {
|
||||
const [copiedColor, setCopiedColor] = useState<string | null>(null);
|
||||
const [hoveredColor, setHoveredColor] = useState<string | null>(null);
|
||||
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const handleColorClick = (color: string) => {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(color).then(() => {
|
||||
setCopiedColor(color);
|
||||
setTimeout(() => setCopiedColor(null), 1500);
|
||||
});
|
||||
} else {
|
||||
// Fallback method for iOS and other unsupported browsers
|
||||
const textArea = textAreaRef.current;
|
||||
if (textArea) {
|
||||
textArea.value = color;
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
setCopiedColor(color);
|
||||
setTimeout(() => setCopiedColor(null), 1500);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy color: ', err);
|
||||
}
|
||||
textArea.blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sizeClasses = size === 'small' ? 'w-full pt-full' : 'w-8 h-8 pt-full';
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`grid grid-cols-8 gap-2 ${size === 'large' ? 'mb-4' : 'mb-2'} z-10`}>
|
||||
{colors.map((color, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`${sizeClasses} rounded-sm cursor-pointer relative group`}
|
||||
style={{backgroundColor: color}}
|
||||
onClick={() => handleColorClick(color)}
|
||||
onMouseEnter={() => setHoveredColor(color)}
|
||||
onMouseLeave={() => setHoveredColor(null)}
|
||||
>
|
||||
{size === 'small' && hoveredColor === color && (
|
||||
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-white dark:bg-gray-800 rounded shadow-lg z-10">
|
||||
<div className="w-4 h-4 rounded-sm mb-1" style={{backgroundColor: color}}></div>
|
||||
<span className="text-xs">{color}</span>
|
||||
</div>
|
||||
)}
|
||||
{size === 'large' && (
|
||||
<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>
|
||||
)}
|
||||
{copiedColor === color && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-70 text-white text-xs">
|
||||
Copied!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<textarea
|
||||
ref={textAreaRef}
|
||||
style={{ position: 'absolute', left: '-9999px' }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorPalette;
|
@ -4,6 +4,7 @@ import { ColorScheme } from '../utils/colorSchemes';
|
||||
import { generateYAML } from '../utils/yamlExport';
|
||||
import { Highlight, themes } from 'prism-react-renderer';
|
||||
import { motion, useAnimation } from 'framer-motion';
|
||||
import ColorPalette from './ColorPalette';
|
||||
|
||||
interface ColorSchemeCardProps {
|
||||
scheme: ColorScheme;
|
||||
@ -127,19 +128,10 @@ fetchData().then(data => console.log(data)); `;
|
||||
)}
|
||||
</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>
|
||||
<ColorPalette
|
||||
colors={Object.values(scheme.colors.normal).concat(Object.values(scheme.colors.bright))}
|
||||
size="large"
|
||||
/>
|
||||
<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"
|
||||
|
69
app/components/HistoryPopup.tsx
Normal file
69
app/components/HistoryPopup.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ColorScheme } from '../utils/colorSchemes';
|
||||
import { generateYAML } from '../utils/yamlExport';
|
||||
import Image from 'next/image';
|
||||
import ColorPalette from './ColorPalette';
|
||||
|
||||
interface HistoryPopupProps {
|
||||
likedSchemes: ColorScheme[];
|
||||
dislikedSchemes: ColorScheme[];
|
||||
onClose: () => void;
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
|
||||
const HistoryPopup: React.FC<HistoryPopupProps> = ({ likedSchemes, dislikedSchemes, onClose, isDarkMode }) => {
|
||||
const [copiedColor, setCopiedColor] = useState<string | null>(null);
|
||||
|
||||
const handleDownload = (scheme: ColorScheme) => {
|
||||
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 renderSchemeGrid = (schemes: ColorScheme[], title: string) => (
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-3">{title}</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 mb-6">
|
||||
{schemes.map((scheme, index) => (
|
||||
<div key={`${scheme.name}-${index}`} className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h3 className="text-sm font-semibold mb-2 truncate">{scheme.name}</h3>
|
||||
<ColorPalette
|
||||
colors={Object.values(scheme.colors.normal).concat(Object.values(scheme.colors.bright))}
|
||||
size="small"
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleDownload(scheme)}
|
||||
className="w-full bg-blue-500 text-white text-xs py-1 px-2 rounded hover:bg-blue-600 transition-colors duration-300"
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl w-[90vw] h-[90vh] overflow-y-auto">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-2xl font-bold">Color Scheme History</h2>
|
||||
<button onClick={onClose} className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<Image src={isDarkMode ? "/close-icon-dark.svg" : "/close-icon-light.svg"} alt="Close" width={24} height={24} />
|
||||
</button>
|
||||
</div>
|
||||
{renderSchemeGrid(likedSchemes, "Liked Schemes")}
|
||||
{renderSchemeGrid(dislikedSchemes, "Disliked Schemes")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HistoryPopup;
|
Reference in New Issue
Block a user