MVP Complete
This commit is contained in:
19
app/components/ActionButton.tsx
Normal file
19
app/components/ActionButton.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
interface ActionButtonProps {
|
||||
onClick: () => void;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const ActionButton: React.FC<ActionButtonProps> = ({ onClick, label }) => {
|
||||
return (
|
||||
<button
|
||||
className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition-colors duration-300"
|
||||
onClick={onClick}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionButton;
|
116
app/components/ColorSchemeCard.tsx
Normal file
116
app/components/ColorSchemeCard.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
import { ColorScheme } from '../utils/colorSchemes';
|
||||
import { generateYAML } from '../utils/yamlExport';
|
||||
import { Highlight, themes } from 'prism-react-renderer';
|
||||
|
||||
interface ColorSchemeCardProps {
|
||||
scheme: ColorScheme;
|
||||
isSelected: boolean;
|
||||
onSelect: () => void;
|
||||
}
|
||||
|
||||
const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, isSelected, onSelect }) => {
|
||||
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);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`w-96 bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden hover:shadow-xl transition-all duration-300 ease-in-out cursor-pointer ${isSelected ? 'ring-4 ring-blue-500' : ''}`}
|
||||
onClick={onSelect}
|
||||
>
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<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="/download-icon.svg" alt="Download" width={24} height={24} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-md mb-4 transition-colors duration-300">
|
||||
<Highlight theme={themes.dracula} code={codeExample} language="javascript">
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<pre className={`${className} text-sm overflow-x-auto`} style={{ ...style, backgroundColor: scheme.colors.primary.background }}>
|
||||
{tokens.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">
|
||||
{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-xs">
|
||||
{color}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorSchemeCard;
|
43
app/components/Settings.tsx
Normal file
43
app/components/Settings.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
|
||||
interface SettingsProps {
|
||||
isDarkMode: boolean;
|
||||
onToggleDarkMode: () => void;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const Settings: React.FC<SettingsProps> = ({ isDarkMode, onToggleDarkMode, isOpen, onClose }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
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">
|
||||
<h2 className="text-xl font-bold mb-4">Settings</h2>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span>Dark Mode</span>
|
||||
<button
|
||||
onClick={onToggleDarkMode}
|
||||
className={`w-12 h-6 rounded-full p-1 transition-colors duration-300 ease-in-out ${
|
||||
isDarkMode ? 'bg-blue-600' : 'bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`w-4 h-4 rounded-full bg-white transform transition-transform duration-300 ease-in-out ${
|
||||
isDarkMode ? 'translate-x-6' : ''
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="mt-4 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition-colors duration-300"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
16
app/components/SettingsIcon.tsx
Normal file
16
app/components/SettingsIcon.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface SettingsIconProps {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const SettingsIcon: React.FC<SettingsIconProps> = ({ onClick }) => {
|
||||
return (
|
||||
<button className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" onClick={onClick}>
|
||||
<Image src="/settings-icon.svg" alt="Settings" width={24} height={24} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsIcon;
|
Reference in New Issue
Block a user