377 lines
14 KiB
TypeScript
377 lines
14 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Search, Grid, List, User, Plus, FolderOpen, Image, Upload } from 'lucide-react';
|
|
|
|
const InspirationEngine = () => {
|
|
const [activeView, setActiveView] = useState('library');
|
|
const [showDropzone, setShowDropzone] = useState(false);
|
|
|
|
// Warm Creative Palette
|
|
const colors = {
|
|
bg: '#1c1917',
|
|
surface: '#292524',
|
|
text: '#fafaf9',
|
|
textMuted: '#a8a29e',
|
|
border: '#44403c',
|
|
primary: '#f97316',
|
|
primaryHover: '#ea580c',
|
|
accent: '#fbbf24'
|
|
};
|
|
|
|
// Mock data for library items
|
|
const mockItems = [
|
|
{ id: 1, height: 200, width: 'normal' },
|
|
{ id: 2, height: 280, width: 'wide' },
|
|
{ id: 3, height: 160, width: 'normal' },
|
|
{ id: 4, height: 240, width: 'normal' },
|
|
{ id: 5, height: 200, width: 'normal' },
|
|
{ id: 6, height: 300, width: 'wide' },
|
|
{ id: 7, height: 180, width: 'normal' },
|
|
{ id: 8, height: 220, width: 'normal' },
|
|
{ id: 9, height: 260, width: 'wide' },
|
|
{ id: 10, height: 190, width: 'normal' },
|
|
{ id: 11, height: 240, width: 'normal' },
|
|
{ id: 12, height: 200, width: 'wide' },
|
|
{ id: 13, height: 220, width: 'normal' },
|
|
{ id: 14, height: 180, width: 'normal' },
|
|
{ id: 15, height: 260, width: 'normal' },
|
|
{ id: 16, height: 200, width: 'wide' },
|
|
{ id: 17, height: 240, width: 'normal' },
|
|
{ id: 18, height: 180, width: 'normal' },
|
|
];
|
|
|
|
// Mock data for collections
|
|
const mockCollections = [
|
|
{ id: 1, name: 'Brand Identity', itemCount: 47, preview: [colors.primary, colors.accent, colors.primaryHover, colors.primary] },
|
|
{ id: 2, name: 'UI Inspiration', itemCount: 132, preview: [colors.accent, colors.primary, colors.accent, colors.primaryHover] },
|
|
{ id: 3, name: 'Typography', itemCount: 28, preview: [colors.primaryHover, colors.primary, colors.accent, colors.primary] },
|
|
{ id: 4, name: 'Color Palettes', itemCount: 64, preview: [colors.primary, colors.primaryHover, colors.accent, colors.primary] },
|
|
{ id: 5, name: 'Illustration', itemCount: 89, preview: [colors.accent, colors.primary, colors.primaryHover, colors.accent] },
|
|
{ id: 6, name: 'Photography', itemCount: 156, preview: [colors.primary, colors.accent, colors.primary, colors.primaryHover] },
|
|
{ id: 7, name: 'Web Design', itemCount: 93, preview: [colors.primaryHover, colors.accent, colors.primary, colors.accent] },
|
|
{ id: 8, name: 'Motion Graphics', itemCount: 41, preview: [colors.primary, colors.primaryHover, colors.accent, colors.primary] },
|
|
];
|
|
|
|
const LibraryView = () => (
|
|
<>
|
|
{/* Compact Horizontal Filters Toolbar */}
|
|
<div className="px-8 py-3 flex items-center gap-2 border-b" style={{ borderColor: colors.border }}>
|
|
<button
|
|
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.textMuted
|
|
}}
|
|
>
|
|
<Grid size={14} />
|
|
Filter
|
|
</button>
|
|
|
|
<button
|
|
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-90 transition-all font-medium"
|
|
style={{
|
|
backgroundColor: colors.primary,
|
|
color: '#fff'
|
|
}}
|
|
>
|
|
All Items
|
|
<span className="text-xs opacity-70">▼</span>
|
|
</button>
|
|
|
|
<button
|
|
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.textMuted
|
|
}}
|
|
>
|
|
Source
|
|
<span className="text-xs opacity-70">▼</span>
|
|
</button>
|
|
|
|
<button
|
|
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.textMuted
|
|
}}
|
|
>
|
|
Media type
|
|
<span className="text-xs opacity-70">▼</span>
|
|
</button>
|
|
|
|
<button
|
|
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.textMuted
|
|
}}
|
|
>
|
|
Date added
|
|
<span className="text-xs opacity-70">▼</span>
|
|
</button>
|
|
|
|
<button
|
|
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.textMuted
|
|
}}
|
|
>
|
|
Tags
|
|
<span className="text-xs opacity-70">▼</span>
|
|
</button>
|
|
|
|
<div className="flex-1" />
|
|
|
|
<span className="text-sm font-medium" style={{ color: colors.textMuted }}>
|
|
1,427 items
|
|
</span>
|
|
|
|
<button
|
|
className="px-3 py-2 hover:opacity-80 transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.text
|
|
}}
|
|
>
|
|
<Grid size={16} />
|
|
</button>
|
|
|
|
<button
|
|
className="px-3 py-2 hover:opacity-80 transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.textMuted
|
|
}}
|
|
>
|
|
<List size={16} />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Main Content - Tightly Packed Masonry Grid */}
|
|
<main className="flex-1 overflow-auto">
|
|
<div className="columns-5 gap-0.5">
|
|
{mockItems.map((item, index) => {
|
|
const demoColors = [colors.primary, colors.accent, colors.surface, colors.primaryHover];
|
|
const itemColor = demoColors[index % demoColors.length];
|
|
|
|
return (
|
|
<div
|
|
key={item.id}
|
|
className="mb-0.5 break-inside-avoid group cursor-pointer relative overflow-hidden transition-all hover:opacity-90"
|
|
style={{
|
|
height: `${item.height}px`,
|
|
backgroundColor: itemColor
|
|
}}
|
|
>
|
|
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-60 transition-all flex items-center justify-center opacity-0 group-hover:opacity-100">
|
|
<button
|
|
className="px-4 py-2 text-sm font-medium hover:scale-105 transition-transform"
|
|
style={{ backgroundColor: colors.text, color: colors.bg }}
|
|
>
|
|
View Details
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</main>
|
|
</>
|
|
);
|
|
|
|
const CollectionsView = () => (
|
|
<>
|
|
{/* Collections Toolbar */}
|
|
<div className="px-8 py-4 flex items-center justify-between border-b" style={{ borderColor: colors.border }}>
|
|
<div className="flex items-center gap-3">
|
|
<h2 className="text-xl font-bold">Your Collections</h2>
|
|
<span className="text-sm" style={{ color: colors.textMuted }}>
|
|
{mockCollections.length} collections
|
|
</span>
|
|
</div>
|
|
|
|
<button
|
|
className="px-4 py-2 flex items-center gap-2 text-sm font-medium hover:opacity-90 transition-all"
|
|
style={{
|
|
backgroundColor: colors.primary,
|
|
color: '#fff'
|
|
}}
|
|
>
|
|
<Plus size={16} />
|
|
New Collection
|
|
</button>
|
|
</div>
|
|
|
|
{/* Collections Grid */}
|
|
<main className="flex-1 overflow-auto">
|
|
<div className="grid grid-cols-4 gap-0.5">
|
|
{mockCollections.map(collection => (
|
|
<div
|
|
key={collection.id}
|
|
className="group cursor-pointer relative"
|
|
>
|
|
{/* Collection Preview Grid */}
|
|
<div
|
|
className="aspect-square overflow-hidden transition-all hover:opacity-90"
|
|
style={{
|
|
backgroundColor: colors.surface
|
|
}}
|
|
>
|
|
<div className="grid grid-cols-2 grid-rows-2 h-full gap-0">
|
|
{collection.preview.map((color, idx) => (
|
|
<div
|
|
key={idx}
|
|
style={{
|
|
backgroundColor: color
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{/* Hover Overlay with Info */}
|
|
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-70 transition-all flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 p-4">
|
|
<h3 className="font-bold text-base mb-2 text-center" style={{ color: colors.text }}>
|
|
{collection.name}
|
|
</h3>
|
|
<div className="flex items-center gap-2 text-sm mb-4" style={{ color: colors.textMuted }}>
|
|
<Image size={14} />
|
|
<span>{collection.itemCount} items</span>
|
|
</div>
|
|
<button
|
|
className="px-4 py-2 text-sm font-medium hover:scale-105 transition-transform"
|
|
style={{ backgroundColor: colors.text, color: colors.bg }}
|
|
>
|
|
Open Collection
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
{/* Create New Collection Card */}
|
|
<div
|
|
className="aspect-square cursor-pointer transition-all hover:opacity-80 flex flex-col items-center justify-center gap-3"
|
|
style={{
|
|
backgroundColor: colors.surface
|
|
}}
|
|
>
|
|
<div
|
|
className="w-12 h-12 flex items-center justify-center"
|
|
style={{
|
|
backgroundColor: colors.primary,
|
|
color: '#fff'
|
|
}}
|
|
>
|
|
<Plus size={24} />
|
|
</div>
|
|
<span className="font-medium text-sm" style={{ color: colors.textMuted }}>
|
|
Create Collection
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</>
|
|
);
|
|
|
|
return (
|
|
<div className="w-full h-screen flex flex-col" style={{ backgroundColor: colors.bg, color: colors.text }}>
|
|
{/* Header */}
|
|
<header className="border-b" style={{ borderColor: colors.border }}>
|
|
<div className="px-8 py-6 flex items-center justify-between">
|
|
<div className="text-2xl font-bold tracking-tight">INSPIRATION</div>
|
|
|
|
{/* Prominent Central Search with Image Toggle */}
|
|
<div className="flex-1 max-w-2xl mx-12 flex gap-3">
|
|
<div className="relative flex-1">
|
|
<Search
|
|
className="absolute left-4 top-1/2 -translate-y-1/2"
|
|
style={{ color: colors.textMuted }}
|
|
size={20}
|
|
/>
|
|
<input
|
|
type="text"
|
|
placeholder="Search by idea, concept, or visual..."
|
|
className="w-full py-4 pl-12 pr-4 text-base transition-all"
|
|
style={{
|
|
backgroundColor: colors.surface,
|
|
color: colors.text,
|
|
border: 'none',
|
|
outline: 'none'
|
|
}}
|
|
/>
|
|
</div>
|
|
<button
|
|
onClick={() => setShowDropzone(!showDropzone)}
|
|
className="px-4 py-4 hover:opacity-90 transition-all"
|
|
style={{
|
|
backgroundColor: showDropzone ? colors.primary : colors.surface,
|
|
color: showDropzone ? '#fff' : colors.textMuted
|
|
}}
|
|
title="Search by image"
|
|
>
|
|
<Image size={20} />
|
|
</button>
|
|
</div>
|
|
|
|
<nav className="flex items-center gap-6">
|
|
<button
|
|
onClick={() => setActiveView('library')}
|
|
className="hover:opacity-70 transition-opacity font-medium"
|
|
style={{ color: activeView === 'library' ? colors.text : colors.textMuted }}
|
|
>
|
|
Library
|
|
</button>
|
|
<button
|
|
onClick={() => setActiveView('collections')}
|
|
className="hover:opacity-70 transition-opacity font-medium"
|
|
style={{ color: activeView === 'collections' ? colors.text : colors.textMuted }}
|
|
>
|
|
Collections
|
|
</button>
|
|
<button className="hover:opacity-70 transition-opacity">
|
|
<User size={22} style={{ color: colors.textMuted }} />
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Expandable Image Search Dropzone */}
|
|
{showDropzone && (
|
|
<div className="px-8 pb-6">
|
|
<div
|
|
className="p-8 text-center cursor-pointer hover:opacity-80 transition-all"
|
|
style={{ backgroundColor: colors.surface }}
|
|
>
|
|
<Upload size={32} style={{ color: colors.primary, margin: '0 auto 12px' }} />
|
|
<p className="font-medium mb-2" style={{ color: colors.text }}>
|
|
Drop your image to search
|
|
</p>
|
|
<p className="text-sm mb-4" style={{ color: colors.textMuted }}>
|
|
Drag and drop, or click to browse
|
|
</p>
|
|
<div className="flex gap-3 justify-center">
|
|
<button
|
|
className="px-4 py-2 text-sm font-medium hover:opacity-90 transition-all"
|
|
style={{ backgroundColor: colors.primary, color: '#fff' }}
|
|
>
|
|
Choose File
|
|
</button>
|
|
<button
|
|
className="px-4 py-2 text-sm font-medium hover:opacity-80 transition-all"
|
|
style={{ backgroundColor: colors.bg, color: colors.textMuted }}
|
|
>
|
|
Paste URL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</header>
|
|
|
|
{/* Active View */}
|
|
{activeView === 'library' ? <LibraryView /> : <CollectionsView />}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default InspirationEngine; |