File size: 6,256 Bytes
3d7d9b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { useState } from "react";
import { X, Search, Folder, Grid, Filter, Plus, Tag } from "lucide-react";
import { useAppStore } from "../store";

const ALL_TAGS = [
  "anatomy",
  "environment",
  "character",
  "lighting",
  "cyberpunk",
  "fantasy",
  "sci-fi",
];

const mockLibrary = Array.from({ length: 24 }).map((_, i) => ({
  url: `https://picsum.photos/id/${100 + i}/400/400`,
  tags: [ALL_TAGS[i % ALL_TAGS.length], ALL_TAGS[(i + 2) % ALL_TAGS.length]],
}));

export const LibraryPanel = () => {
  const { isLibraryOpen, setIsLibraryOpen, setImages, pan, zoom } =
    useAppStore();
  const [search, setSearch] = useState("");
  const [activeTag, setActiveTag] = useState<string | null>(null);

  const handleAdd = (src: string) => {
    const newImg = {
      id: Math.random().toString(36).substr(2, 9),
      url: src,
      x: (-pan.x + window.innerWidth / 4) / zoom,
      y: (-pan.y + window.innerHeight / 4) / zoom,
      width: 250,
      height: 250,
      aspectRatio: 1,
    };
    setImages((prev) => [...prev, newImg]);
  };

  const filteredLibrary = mockLibrary.filter((img) => {
    if (activeTag && !img.tags.includes(activeTag)) return false;
    if (search && !img.tags.some((t) => t.includes(search.toLowerCase())))
      return false;
    return true;
  });

  return (
    <div

      className={`absolute left-0 top-0 h-full w-[45%] max-w-[500px] bg-panel-bg shadow-2xl flex flex-col z-[60] transform transition-transform duration-500 ease-[cubic-bezier(0.19,1,0.22,1)] ${isLibraryOpen ? "translate-x-0" : "-translate-x-full"}`}

    >

      <div className="flex items-center justify-between p-4 bg-panel-bg z-10 border-b border-[#3A3A3E]">

        <div className="flex items-center gap-2 text-[#E0E0E0] text-[14px] font-medium tracking-tight">

          <Folder size={16} className="text-[#0A84FF]" />

          Asset Library

        </div>

        <button

          onClick={() => setIsLibraryOpen(false)}

          className="text-[#A0A0A0] hover:text-[#E0E0E0] p-1.5 rounded-md hover:bg-white/5 transition-colors"

        >

          <X size={16} />

        </button>

      </div>



      <div className="px-4 py-3 bg-[#2A2A2E] flex flex-col gap-3">

        <div className="relative group">

          <Search

            size={14}

            className="absolute left-3 top-1/2 -translate-y-1/2 text-[#808080] group-focus-within:text-[#0A84FF] transition-colors"

          />

          <input

            type="text"

            value={search}

            onChange={(e) => setSearch(e.target.value)}

            placeholder="Search assets or tags..."

            className="w-full bg-[#1C1C1E] text-[#E0E0E0] pl-9 pr-3 py-2 text-[13px] rounded-lg border border-[#3A3A3E] focus:border-[#0A84FF] outline-none transition-all placeholder:text-[#808080]"

          />

        </div>



        {/* Tag Pills */}

        <div className="flex items-center gap-1.5 overflow-x-auto hide-scrollbar pb-1">

          <button

            onClick={() => setActiveTag(null)}

            className={`whitespace-nowrap px-3 py-1 rounded-full text-[11px] font-medium transition-colors ${!activeTag ? "bg-[#0A84FF] text-white" : "bg-[#3A3A3E] text-[#C0C0C0] hover:bg-[#4A4A4E]"}`}

          >

            All

          </button>

          {ALL_TAGS.map((tag) => (

            <button

              key={tag}

              onClick={() => setActiveTag(tag)}

              className={`whitespace-nowrap px-3 py-1 rounded-full text-[11px] font-medium transition-colors flex items-center gap-1 ${activeTag === tag ? "bg-[#0A84FF] text-white" : "bg-[#3A3A3E] text-[#C0C0C0] hover:bg-[#4A4A4E]"}`}

            >

              <Tag size={10} />

              {tag}

            </button>

          ))}

        </div>

      </div>



      <div className="flex-1 overflow-y-auto bg-[#1C1C1E] p-4 custom-scrollbar">

        <div className="flex justify-between items-center mb-4">

          <span className="text-[11px] font-medium text-[#808080] uppercase tracking-widest">

            Images ({filteredLibrary.length})

          </span>

          <Grid size={14} className="text-[#808080]" />

        </div>

        <div className="grid grid-cols-3 gap-3">

          <div className="aspect-square bg-white/5 hover:bg-white/10 border border-dashed border-white/20 hover:border-white/30 rounded-xl cursor-pointer flex flex-col items-center justify-center text-[#A0A0A0] hover:text-[#E0E0E0] transition-all group shadow-sm">

            <div className="w-8 h-8 rounded-full bg-black/20 flex items-center justify-center mb-2 group-hover:scale-110 transition-transform">

              <Plus size={16} />

            </div>

            <span className="text-[11px] font-medium">Upload File</span>

          </div>

          {filteredLibrary.map((img, i) => (

            <div

              key={i}

              className="aspect-square bg-[#2A2A2E] rounded-xl cursor-pointer group relative overflow-hidden shadow-sm ring-1 ring-[#3A3A3E] hover:ring-[#0A84FF] transition-all"

              onClick={() => handleAdd(img.url)}

            >

              <img

                src={img.url}

                className="w-full h-full object-cover opacity-90 group-hover:opacity-100 group-hover:scale-105 transition-all duration-500 ease-out"

              />

              <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity flex flex-col justify-end p-2 pointer-events-none">

                <span className="text-[10px] text-white font-medium truncate mb-1">

                  IMG_{100 + i}.jpg

                </span>

                <div className="flex items-center gap-1 overflow-hidden">

                  {img.tags.map((t) => (

                    <span

                      key={t}

                      className="text-[9px] bg-white/20 text-white/90 px-1.5 py-0.5 rounded backdrop-blur"

                    >

                      {t}

                    </span>

                  ))}

                </div>

              </div>

            </div>

          ))}

        </div>

      </div>

    </div>
  );
};