refactor: extract CodeSnippet to shared ui component
Move components/favicon/CodeSnippet.tsx → components/ui/code-snippet.tsx. Update Favicon tool import path. Replace Animate tool's local CodeBlock (with external copy/download buttons) with the shared CodeSnippet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { Copy, Download } from 'lucide-react';
|
|
||||||
import { toast } from 'sonner';
|
|
||||||
import { cn } from '@/lib/utils/cn';
|
import { cn } from '@/lib/utils/cn';
|
||||||
import { buildCSS, buildTailwindCSS } from '@/lib/animate/cssBuilder';
|
import { buildCSS, buildTailwindCSS } from '@/lib/animate/cssBuilder';
|
||||||
|
import { CodeSnippet } from '@/components/ui/code-snippet';
|
||||||
import type { AnimationConfig } from '@/types/animate';
|
import type { AnimationConfig } from '@/types/animate';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -13,45 +12,6 @@ interface Props {
|
|||||||
|
|
||||||
type ExportTab = 'css' | 'tailwind';
|
type ExportTab = 'css' | 'tailwind';
|
||||||
|
|
||||||
const actionBtn =
|
|
||||||
'flex items-center justify-center gap-1.5 px-3 py-1.5 text-xs glass rounded-md border border-border/30 text-muted-foreground hover:text-primary hover:border-primary/30 hover:bg-primary/10 transition-all';
|
|
||||||
|
|
||||||
function CodeBlock({ code, filename }: { code: string; filename: string }) {
|
|
||||||
const copy = () => {
|
|
||||||
navigator.clipboard.writeText(code);
|
|
||||||
toast.success('Copied to clipboard!');
|
|
||||||
};
|
|
||||||
|
|
||||||
const download = () => {
|
|
||||||
const blob = new Blob([code], { type: 'text/css' });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = filename;
|
|
||||||
a.click();
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
toast.success(`Downloaded ${filename}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="relative group rounded-xl overflow-hidden border border-white/5" style={{ background: '#06060e' }}>
|
|
||||||
<pre className="p-4 overflow-x-auto font-mono text-[11px] text-white/55 leading-relaxed max-h-64 scrollbar">
|
|
||||||
<code>{code}</code>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button onClick={copy} className={cn(actionBtn, 'flex-1')}>
|
|
||||||
<Copy className="w-3 h-3" />Copy
|
|
||||||
</button>
|
|
||||||
<button onClick={download} className={cn(actionBtn, 'flex-1')}>
|
|
||||||
<Download className="w-3 h-3" />Download .css
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ExportPanel({ config }: Props) {
|
export function ExportPanel({ config }: Props) {
|
||||||
const [tab, setTab] = useState<ExportTab>('css');
|
const [tab, setTab] = useState<ExportTab>('css');
|
||||||
const css = useMemo(() => buildCSS(config), [config]);
|
const css = useMemo(() => buildCSS(config), [config]);
|
||||||
@@ -76,8 +36,8 @@ export function ExportPanel({ config }: Props) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{tab === 'css' && <CodeBlock code={css} filename={`${config.name}.css`} />}
|
{tab === 'css' && <CodeSnippet code={css} maxHeight="16rem" />}
|
||||||
{tab === 'tailwind' && <CodeBlock code={tailwind} filename={`${config.name}.tailwind.css`} />}
|
{tab === 'tailwind' && <CodeSnippet code={tailwind} maxHeight="16rem" />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import * as React from 'react';
|
|||||||
import { Download, Loader2, Code2, Globe, Layout, FileImage } from 'lucide-react';
|
import { Download, Loader2, Code2, Globe, Layout, FileImage } from 'lucide-react';
|
||||||
import { FaviconFileUpload } from './FaviconFileUpload';
|
import { FaviconFileUpload } from './FaviconFileUpload';
|
||||||
import { ColorInput } from '@/components/ui/color-input';
|
import { ColorInput } from '@/components/ui/color-input';
|
||||||
import { CodeSnippet } from './CodeSnippet';
|
import { CodeSnippet } from '@/components/ui/code-snippet';
|
||||||
import { generateFaviconSet } from '@/lib/favicon/faviconService';
|
import { generateFaviconSet } from '@/lib/favicon/faviconService';
|
||||||
import { downloadBlobsAsZip } from '@/lib/media/utils/fileUtils';
|
import { downloadBlobsAsZip } from '@/lib/media/utils/fileUtils';
|
||||||
import type { FaviconSet, FaviconOptions } from '@/types/favicon';
|
import type { FaviconSet, FaviconOptions } from '@/types/favicon';
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import { toast } from 'sonner';
|
|||||||
|
|
||||||
interface CodeSnippetProps {
|
interface CodeSnippetProps {
|
||||||
code: string;
|
code: string;
|
||||||
|
maxHeight?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CodeSnippet({ code }: CodeSnippetProps) {
|
export function CodeSnippet({ code, maxHeight }: CodeSnippetProps) {
|
||||||
const [copied, setCopied] = React.useState(false);
|
const [copied, setCopied] = React.useState(false);
|
||||||
|
|
||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
@@ -22,12 +23,15 @@ export function CodeSnippet({ code }: CodeSnippetProps) {
|
|||||||
<div className="relative group rounded-xl overflow-hidden border border-white/5" style={{ background: '#06060e' }}>
|
<div className="relative group rounded-xl overflow-hidden border border-white/5" style={{ background: '#06060e' }}>
|
||||||
<button
|
<button
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
className="absolute right-3 top-3 opacity-0 group-hover:opacity-100 flex items-center gap-1 px-2 py-1 text-[10px] font-mono rounded-md border border-white/10 bg-white/5 text-white/40 hover:text-white/70 hover:border-white/20 transition-all"
|
className="absolute right-3 top-3 opacity-0 group-hover:opacity-100 flex items-center gap-1 px-2 py-1 text-[10px] font-mono rounded-md border border-white/10 bg-white/5 text-white/40 hover:text-white/70 hover:border-white/20 transition-all z-10"
|
||||||
>
|
>
|
||||||
{copied ? <Check className="w-2.5 h-2.5" /> : <Copy className="w-2.5 h-2.5" />}
|
{copied ? <Check className="w-2.5 h-2.5" /> : <Copy className="w-2.5 h-2.5" />}
|
||||||
{copied ? 'Copied' : 'Copy'}
|
{copied ? 'Copied' : 'Copy'}
|
||||||
</button>
|
</button>
|
||||||
<pre className="p-4 overflow-x-auto font-mono text-[11px] text-white/55 leading-relaxed">
|
<pre
|
||||||
|
className="p-4 overflow-x-auto font-mono text-[11px] text-white/55 leading-relaxed"
|
||||||
|
style={maxHeight ? { maxHeight, overflowY: 'auto' } : undefined}
|
||||||
|
>
|
||||||
<code>{code}</code>
|
<code>{code}</code>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user