Files
pastel-ui/components/tools/ExportMenu.tsx
valknarness 89bd011537 feat: add comprehensive export functionality for palettes
Implement full palette export system with multiple formats:

**Export Utilities (lib/utils/export.ts):**
- exportAsCSS() - CSS custom properties (:root variables)
- exportAsSCSS() - SCSS variables ($color-name format)
- exportAsTailwind() - Tailwind config module export
- exportAsJSON() - Structured JSON with color objects
- exportAsJavaScript() - JavaScript array of colors
- downloadAsFile() - Browser download helper
- ExportColor interface for typed exports

**ExportMenu Component:**
- Format selector dropdown (5 formats)
- Live preview of export code
- Syntax-highlighted code display
- Copy to clipboard button
- Download as file button
- Success feedback (Check icon + toast)
- Responsive layout
- Conditional rendering (empty state)

**Export Formats:**

1. CSS Variables:
   ```css
   :root {
     --color-1: #ff0099;
     --color-2: #0099ff;
   }
   ```

2. SCSS Variables:
   ```scss
   $color-1: #ff0099;
   $color-2: #0099ff;
   ```

3. Tailwind Config:
   ```js
   module.exports = {
     theme: {
       extend: {
         colors: {
           'color-1': '#ff0099',
         },
       },
     },
   };
   ```

4. JSON:
   ```json
   {
     "colors": [
       { "name": "color-1", "hex": "#ff0099" }
     ]
   }
   ```

5. JavaScript Array:
   ```js
   const colors = ['#ff0099', '#0099ff'];
   ```

**Integration:**
- Added to Gradient Creator page
- Added to Distinct Colors page
- Conditional rendering (shows only when colors exist)
- Proper spacing and layout

**Features:**
- Real-time preview of generated code
- Copy with toast notification
- Download with proper file extensions
- Format-specific filenames
- Clean, readable output
- Proper indentation and formatting

**User Experience:**
- Select format from dropdown
- See live preview immediately
- One-click copy or download
- Success feedback
- Professional code formatting

Build successful! Export functionality working on all palette pages.

Next: Keyboard shortcuts and URL sharing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 12:19:52 +01:00

126 lines
3.3 KiB
TypeScript

'use client';
import { Button } from '@/components/ui/button';
import { Select } from '@/components/ui/select';
import { useState } from 'react';
import { Download, Copy, Check } from 'lucide-react';
import { toast } from 'sonner';
import {
exportAsCSS,
exportAsSCSS,
exportAsTailwind,
exportAsJSON,
exportAsJavaScript,
downloadAsFile,
type ExportColor,
} from '@/lib/utils/export';
interface ExportMenuProps {
colors: string[];
className?: string;
}
type ExportFormat = 'css' | 'scss' | 'tailwind' | 'json' | 'javascript';
export function ExportMenu({ colors, className }: ExportMenuProps) {
const [format, setFormat] = useState<ExportFormat>('css');
const [copied, setCopied] = useState(false);
const exportColors: ExportColor[] = colors.map((hex) => ({ hex }));
const getExportContent = (): string => {
switch (format) {
case 'css':
return exportAsCSS(exportColors);
case 'scss':
return exportAsSCSS(exportColors);
case 'tailwind':
return exportAsTailwind(exportColors);
case 'json':
return exportAsJSON(exportColors);
case 'javascript':
return exportAsJavaScript(exportColors);
}
};
const getFileExtension = (): string => {
switch (format) {
case 'css':
return 'css';
case 'scss':
return 'scss';
case 'tailwind':
return 'js';
case 'json':
return 'json';
case 'javascript':
return 'js';
}
};
const handleCopy = () => {
const content = getExportContent();
navigator.clipboard.writeText(content);
setCopied(true);
toast.success('Copied to clipboard!');
setTimeout(() => setCopied(false), 2000);
};
const handleDownload = () => {
const content = getExportContent();
const extension = getFileExtension();
downloadAsFile(content, `palette.${extension}`, 'text/plain');
toast.success('Downloaded!');
};
if (colors.length === 0) {
return null;
}
return (
<div className={className}>
<div className="space-y-4">
<div>
<h3 className="text-sm font-medium mb-2">Export Palette</h3>
<Select
value={format}
onChange={(e) => setFormat(e.target.value as ExportFormat)}
>
<option value="css">CSS Variables</option>
<option value="scss">SCSS Variables</option>
<option value="tailwind">Tailwind Config</option>
<option value="json">JSON</option>
<option value="javascript">JavaScript Array</option>
</Select>
</div>
<div className="p-4 bg-muted rounded-lg">
<pre className="text-xs overflow-x-auto">
<code>{getExportContent()}</code>
</pre>
</div>
<div className="flex gap-2">
<Button onClick={handleCopy} variant="outline" className="flex-1">
{copied ? (
<>
<Check className="h-4 w-4 mr-2" />
Copied!
</>
) : (
<>
<Copy className="h-4 w-4 mr-2" />
Copy
</>
)}
</Button>
<Button onClick={handleDownload} variant="default" className="flex-1">
<Download className="h-4 w-4 mr-2" />
Download
</Button>
</div>
</div>
</div>
);
}