@medialane/ui 0.252.0 → 0.254.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/components/account/input-otp.d.cts +2 -2
  2. package/dist/components/account/input-otp.d.ts +2 -2
  3. package/dist/components/button.d.cts +2 -2
  4. package/dist/components/button.d.ts +2 -2
  5. package/dist/components/collection-card.cjs +11 -19
  6. package/dist/components/collection-card.cjs.map +1 -1
  7. package/dist/components/collection-card.js +11 -19
  8. package/dist/components/collection-card.js.map +1 -1
  9. package/dist/components/collections-page.cjs +181 -139
  10. package/dist/components/collections-page.cjs.map +1 -1
  11. package/dist/components/collections-page.js +172 -140
  12. package/dist/components/collections-page.js.map +1 -1
  13. package/dist/components/creator-page.cjs +1 -1
  14. package/dist/components/creator-page.cjs.map +1 -1
  15. package/dist/components/creator-page.js +1 -1
  16. package/dist/components/creator-page.js.map +1 -1
  17. package/dist/components/drop/create/drop-create.cjs +1 -1
  18. package/dist/components/drop/create/drop-create.cjs.map +1 -1
  19. package/dist/components/drop/create/drop-create.js +1 -1
  20. package/dist/components/drop/create/drop-create.js.map +1 -1
  21. package/dist/components/drop/create/launch-dialog.cjs +1 -1
  22. package/dist/components/drop/create/launch-dialog.cjs.map +1 -1
  23. package/dist/components/drop/create/launch-dialog.js +1 -1
  24. package/dist/components/drop/create/launch-dialog.js.map +1 -1
  25. package/dist/components/fast-mint/fast-mint.d.cts +2 -2
  26. package/dist/components/fast-mint/fast-mint.d.ts +2 -2
  27. package/dist/components/hero-slider.cjs +2 -1
  28. package/dist/components/hero-slider.cjs.map +1 -1
  29. package/dist/components/hero-slider.js +2 -1
  30. package/dist/components/hero-slider.js.map +1 -1
  31. package/dist/components/launchpad/edition-mint-schema.d.cts +2 -2
  32. package/dist/components/launchpad/edition-mint-schema.d.ts +2 -2
  33. package/dist/components/sheet.d.cts +1 -1
  34. package/dist/components/sheet.d.ts +1 -1
  35. package/dist/utils/use-collections.cjs +1 -2
  36. package/dist/utils/use-collections.cjs.map +1 -1
  37. package/dist/utils/use-collections.js +1 -2
  38. package/dist/utils/use-collections.js.map +1 -1
  39. package/package.json +1 -1
@@ -1,17 +1,25 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useRef } from "react";
4
- import { Layers, BadgeCheck, Eye, SlidersHorizontal, Award, Sparkles, Package } from "lucide-react";
4
+ import useSWR from "swr";
5
+ import { Layers, Eye, SlidersHorizontal, Award, Package, ArrowDownUp, Search, X } from "lucide-react";
5
6
  import { Button } from "./button.js";
6
7
  import { Dialog, DialogContent, DialogTitle } from "./dialog.js";
8
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
7
9
  import { HelpIcon } from "./help-icon.js";
10
+ import { Input } from "./input.js";
11
+ import { ResultThumbnail } from "./result-thumbnail.js";
12
+ import Link from "./link.js";
13
+ import { useRouter } from "./router.js";
8
14
  import { PageContainer } from "./page-container.js";
9
15
  import { CollectionCard, CollectionCardSkeleton } from "./collection-card.js";
10
16
  import { LoadMoreSentinel } from "./load-more-sentinel.js";
11
17
  import { cn } from "../utils/cn.js";
12
18
  import { useCollections } from "../utils/use-collections.js";
13
- import { usePlatformStats } from "../utils/use-platform-stats.js";
14
- const PAGE_SIZE = 18;
19
+ import { useMedialaneClient } from "../utils/use-medialane-client.js";
20
+ import { SEARCH_MIN_LENGTH, useSearch } from "../utils/use-search.js";
21
+ import { collectionHref } from "@medialane/sdk";
22
+ const PAGE_SIZE = 24;
15
23
  const SORT_OPTIONS = [
16
24
  { label: "Recent", value: "recent" },
17
25
  { label: "Most assets", value: "supply" },
@@ -24,12 +32,90 @@ const SERVICE_TABS = [
24
32
  { label: "POP Events", value: "pop-protocol" },
25
33
  { label: "Drops", value: "drop-collection" }
26
34
  ];
35
+ function useStandardCounts() {
36
+ const client = useMedialaneClient();
37
+ const { data } = useSWR(
38
+ "collection-standard-counts",
39
+ async () => {
40
+ const [single, multi] = await Promise.all(
41
+ ["ERC721", "ERC1155"].map(
42
+ (standard) => client.api.collections.list({ standard, hideEmpty: true, limit: 1 })
43
+ )
44
+ );
45
+ return { single: single.meta?.total, multi: multi.meta?.total };
46
+ },
47
+ { revalidateOnFocus: false, dedupingInterval: 6e4 }
48
+ );
49
+ return data ?? null;
50
+ }
51
+ function CollectionSearch() {
52
+ const router = useRouter();
53
+ const [value, setValue] = useState("");
54
+ const [query, setQuery] = useState("");
55
+ const [open, setOpen] = useState(false);
56
+ const { results } = useSearch(query, { limit: 6 });
57
+ const matches = results?.collections ?? [];
58
+ useEffect(() => {
59
+ const timer = setTimeout(() => setQuery(value), 300);
60
+ return () => clearTimeout(timer);
61
+ }, [value]);
62
+ const clear = () => {
63
+ setValue("");
64
+ setQuery("");
65
+ setOpen(false);
66
+ };
67
+ return /* @__PURE__ */ jsxs("div", { className: "relative w-full sm:max-w-sm sm:flex-1", children: [
68
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
69
+ /* @__PURE__ */ jsx(
70
+ Input,
71
+ {
72
+ className: "h-9 pl-9 pr-9 text-sm",
73
+ placeholder: "Search collections",
74
+ "aria-label": "Search collections",
75
+ value,
76
+ onChange: (e) => {
77
+ setValue(e.target.value);
78
+ setOpen(true);
79
+ },
80
+ onFocus: () => setOpen(true),
81
+ onBlur: () => setTimeout(() => setOpen(false), 200),
82
+ onKeyDown: (e) => {
83
+ if (e.key === "Enter" && value.trim().length >= SEARCH_MIN_LENGTH) {
84
+ router.push(`/search?q=${encodeURIComponent(value.trim())}`);
85
+ }
86
+ }
87
+ }
88
+ ),
89
+ value && /* @__PURE__ */ jsx(
90
+ "button",
91
+ {
92
+ "aria-label": "Clear search",
93
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
94
+ onClick: clear,
95
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
96
+ }
97
+ ),
98
+ open && matches.length > 0 && /* @__PURE__ */ jsx("div", { className: "absolute left-0 right-0 top-full z-50 mt-1 overflow-hidden rounded-xl border border-border bg-popover shadow-lg", children: matches.map((col) => {
99
+ const label = col.name ?? "Untitled";
100
+ return /* @__PURE__ */ jsxs(
101
+ Link,
102
+ {
103
+ href: collectionHref("STARKNET", col.contractAddress),
104
+ className: "flex items-center gap-3 px-3 py-2.5 transition-colors hover:bg-muted/50",
105
+ children: [
106
+ /* @__PURE__ */ jsx(ResultThumbnail, { src: col.image, label }),
107
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", children: label })
108
+ ]
109
+ },
110
+ col.contractAddress
111
+ );
112
+ }) })
113
+ ] });
114
+ }
27
115
  function CollectionsPage() {
28
- const { stats } = usePlatformStats();
116
+ const counts = useStandardCounts();
29
117
  const [sort, setSort] = useState("recent");
30
- const [featured, setFeatured] = useState(false);
31
118
  const [hideEmpty, setHideEmpty] = useState(true);
32
- const [exclusive, setExclusive] = useState(false);
33
119
  const [service, setService] = useState(void 0);
34
120
  const [filtersOpen, setFiltersOpen] = useState(false);
35
121
  const [page, setPage] = useState(1);
@@ -37,21 +123,30 @@ function CollectionsPage() {
37
123
  const { collections, meta, isLoading } = useCollections(
38
124
  page,
39
125
  PAGE_SIZE,
40
- featured ? true : void 0,
126
+ void 0,
41
127
  sort,
42
128
  service === "pop-protocol" || service === "drop-collection" ? false : hideEmpty,
43
129
  service,
44
130
  "ERC721,ERC1155"
45
131
  );
46
- const prevFilters = useRef({ sort, featured, hideEmpty, service });
132
+ const { collections: featuredCollections, isLoading: featuredLoading } = useCollections(
133
+ 1,
134
+ 8,
135
+ true,
136
+ "recent",
137
+ true,
138
+ void 0,
139
+ "ERC721,ERC1155"
140
+ );
141
+ const prevFilters = useRef({ sort, hideEmpty, service });
47
142
  useEffect(() => {
48
143
  const f = prevFilters.current;
49
- if (f.sort !== sort || f.featured !== featured || f.hideEmpty !== hideEmpty || f.service !== service) {
50
- prevFilters.current = { sort, featured, hideEmpty, service };
144
+ if (f.sort !== sort || f.hideEmpty !== hideEmpty || f.service !== service) {
145
+ prevFilters.current = { sort, hideEmpty, service };
51
146
  setPage(1);
52
147
  setAllCollections([]);
53
148
  }
54
- }, [sort, featured, hideEmpty, service]);
149
+ }, [sort, hideEmpty, service]);
55
150
  useEffect(() => {
56
151
  if (isLoading || collections.length === 0) return;
57
152
  setAllCollections((prev) => {
@@ -62,13 +157,9 @@ function CollectionsPage() {
62
157
  }, [collections, isLoading, page]);
63
158
  const hasMore = meta?.total != null ? allCollections.length < meta.total : false;
64
159
  const isInitialLoading = isLoading && allCollections.length === 0;
65
- const activeFilters = [sort !== "recent", featured, !hideEmpty, exclusive].filter(Boolean).length;
66
- const displayCollections = exclusive ? allCollections.filter((c) => !!c.profile?.hasGatedContent) : allCollections;
160
+ const activeFilters = [!hideEmpty].filter(Boolean).length;
67
161
  const resetAll = () => {
68
- setSort("recent");
69
- setFeatured(false);
70
162
  setHideEmpty(true);
71
- setExclusive(false);
72
163
  };
73
164
  const handleServiceChange = (val) => {
74
165
  setService(val);
@@ -77,58 +168,55 @@ function CollectionsPage() {
77
168
  /* @__PURE__ */ jsxs("div", { className: "space-y-2 pt-8", children: [
78
169
  /* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold", children: "Onchain Collections" }),
79
170
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 mt-1", children: [
80
- meta?.total != null && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm", children: [
81
- /* @__PURE__ */ jsx("span", { className: "font-bold tabular-nums", children: (meta.total ?? 0).toLocaleString() }),
82
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Collections" })
83
- ] }),
84
- stats?.tokens != null && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm", children: [
85
- /* @__PURE__ */ jsx("span", { className: "font-bold tabular-nums", children: stats.tokens.toLocaleString() }),
86
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Assets" })
171
+ counts?.single != null && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm", children: [
172
+ /* @__PURE__ */ jsx("span", { className: "font-bold tabular-nums", children: counts.single.toLocaleString() }),
173
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Single NFTs" })
87
174
  ] }),
88
- stats?.sales != null && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm", children: [
89
- /* @__PURE__ */ jsx("span", { className: "font-bold tabular-nums", children: stats.sales.toLocaleString() }),
90
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Sales" })
175
+ counts?.multi != null && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm", children: [
176
+ /* @__PURE__ */ jsx("span", { className: "font-bold tabular-nums", children: counts.multi.toLocaleString() }),
177
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Multi editions" })
91
178
  ] })
92
179
  ] })
93
180
  ] }),
94
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pb-3 border-b border-border/60 flex-wrap", children: [
95
- /* @__PURE__ */ jsxs(
96
- "button",
97
- {
98
- onClick: () => setFiltersOpen(true),
99
- className: cn(
100
- "relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors",
101
- activeFilters > 0 || service !== void 0 ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
102
- ),
103
- children: [
104
- /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
105
- "Filters",
106
- (activeFilters > 0 || service !== void 0) && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground", children: activeFilters + (service !== void 0 ? 1 : 0) })
107
- ]
108
- }
109
- ),
110
- service !== void 0 && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
111
- service === "drop-collection" ? /* @__PURE__ */ jsx(Package, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Award, { className: "h-3 w-3" }),
112
- SERVICE_TABS.find((t) => t.value === service)?.label,
113
- /* @__PURE__ */ jsx("button", { onClick: () => setService(void 0), className: "ml-0.5 hover:text-primary/60", children: "\xD7" })
114
- ] }),
115
- sort !== "recent" && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
116
- SORT_OPTIONS.find((o) => o.value === sort)?.label,
117
- /* @__PURE__ */ jsx("button", { onClick: () => setSort("recent"), className: "ml-0.5 hover:text-primary/60", children: "\xD7" })
118
- ] }),
119
- featured && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
120
- /* @__PURE__ */ jsx(BadgeCheck, { className: "h-3 w-3" }),
121
- "Featured",
122
- /* @__PURE__ */ jsx("button", { onClick: () => setFeatured(false), className: "ml-0.5 hover:text-primary/60", children: "\xD7" })
123
- ] }),
124
- !hideEmpty && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
125
- "Show empty",
126
- /* @__PURE__ */ jsx("button", { onClick: () => setHideEmpty(true), className: "ml-0.5 hover:text-primary/60", children: "\xD7" })
181
+ (featuredLoading || featuredCollections.length > 0) && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5", children: featuredLoading ? Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(CollectionCardSkeleton, {}, i)) : featuredCollections.map((col) => /* @__PURE__ */ jsx(CollectionCard, { collection: col }, col.contractAddress)) }),
182
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
183
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
184
+ /* @__PURE__ */ jsx(CollectionSearch, {}),
185
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 sm:ml-auto", children: [
186
+ /* @__PURE__ */ jsxs(Select, { value: sort, onValueChange: (v) => setSort(v), children: [
187
+ /* @__PURE__ */ jsxs(SelectTrigger, { className: "h-9 w-auto gap-1.5 text-xs", "aria-label": "Sort collections", children: [
188
+ /* @__PURE__ */ jsx(ArrowDownUp, { className: "h-3.5 w-3.5 text-muted-foreground" }),
189
+ /* @__PURE__ */ jsx(SelectValue, {})
190
+ ] }),
191
+ /* @__PURE__ */ jsx(SelectContent, { children: SORT_OPTIONS.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, className: "text-xs", children: opt.label }, opt.value)) })
192
+ ] }),
193
+ /* @__PURE__ */ jsxs(
194
+ "button",
195
+ {
196
+ onClick: () => setFiltersOpen(true),
197
+ className: cn(
198
+ "relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors",
199
+ activeFilters > 0 || service !== void 0 ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
200
+ ),
201
+ children: [
202
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
203
+ "Filters",
204
+ (activeFilters > 0 || service !== void 0) && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground", children: activeFilters + (service !== void 0 ? 1 : 0) })
205
+ ]
206
+ }
207
+ )
208
+ ] })
127
209
  ] }),
128
- exclusive && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
129
- /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
130
- "Exclusive",
131
- /* @__PURE__ */ jsx("button", { onClick: () => setExclusive(false), className: "ml-0.5 hover:text-primary/60", children: "\xD7" })
210
+ (service !== void 0 || !hideEmpty) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
211
+ service !== void 0 && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
212
+ service === "drop-collection" ? /* @__PURE__ */ jsx(Package, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Award, { className: "h-3 w-3" }),
213
+ SERVICE_TABS.find((t) => t.value === service)?.label,
214
+ /* @__PURE__ */ jsx("button", { onClick: () => setService(void 0), className: "ml-0.5 hover:text-primary/60", "aria-label": "Remove source filter", children: "\xD7" })
215
+ ] }),
216
+ !hideEmpty && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
217
+ "Show empty",
218
+ /* @__PURE__ */ jsx("button", { onClick: () => setHideEmpty(true), className: "ml-0.5 hover:text-primary/60", "aria-label": "Remove show empty filter", children: "\xD7" })
219
+ ] })
132
220
  ] })
133
221
  ] }),
134
222
  /* @__PURE__ */ jsx(Dialog, { open: filtersOpen, onOpenChange: setFiltersOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]", children: [
@@ -162,92 +250,36 @@ function CollectionsPage() {
162
250
  label
163
251
  )) })
164
252
  ] }),
165
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
166
- /* @__PURE__ */ jsx("p", { className: "text-2xs font-semibold text-muted-foreground", children: "Sort" }),
167
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: SORT_OPTIONS.map((opt) => /* @__PURE__ */ jsx(
168
- "button",
169
- {
170
- onClick: () => setSort(opt.value),
171
- className: cn(
172
- "text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap",
173
- sort === opt.value ? "border-primary bg-primary/10 text-primary font-medium" : "border-border text-muted-foreground hover:border-primary/50"
174
- ),
175
- children: opt.label
176
- },
177
- opt.value
178
- )) })
179
- ] }),
180
253
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
181
254
  /* @__PURE__ */ jsx("p", { className: "text-2xs font-semibold text-muted-foreground", children: "Show" }),
182
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
183
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
184
- /* @__PURE__ */ jsxs(
185
- "button",
186
- {
187
- onClick: () => setFeatured((v) => !v),
188
- className: cn(
189
- "flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left",
190
- featured ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50"
191
- ),
192
- children: [
193
- /* @__PURE__ */ jsx(BadgeCheck, { className: "h-4 w-4 shrink-0" }),
194
- "Featured only"
195
- ]
196
- }
197
- ),
198
- /* @__PURE__ */ jsx(HelpIcon, { content: "Show only collections featured by Medialane", side: "left" })
199
- ] }),
200
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
201
- /* @__PURE__ */ jsxs(
202
- "button",
203
- {
204
- onClick: () => setHideEmpty((v) => !v),
205
- className: cn(
206
- "flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left",
207
- !hideEmpty ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50"
208
- ),
209
- children: [
210
- /* @__PURE__ */ jsx(Eye, { className: "h-4 w-4 shrink-0" }),
211
- "Show empty collections"
212
- ]
213
- }
214
- ),
215
- /* @__PURE__ */ jsx(HelpIcon, { content: "Include collections with no minted assets yet \u2014 hidden by default to keep the feed clean", side: "left" })
216
- ] }),
217
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
218
- /* @__PURE__ */ jsxs(
219
- "button",
220
- {
221
- onClick: () => setExclusive((v) => !v),
222
- className: cn(
223
- "flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left",
224
- exclusive ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50"
225
- ),
226
- children: [
227
- /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 shrink-0" }),
228
- "Exclusive content only"
229
- ]
230
- }
231
- ),
232
- /* @__PURE__ */ jsx(HelpIcon, { content: "Show only collections with holder-exclusive content", side: "left" })
233
- ] })
234
- ] })
255
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
256
+ /* @__PURE__ */ jsxs(
257
+ "button",
258
+ {
259
+ onClick: () => setHideEmpty((v) => !v),
260
+ className: cn(
261
+ "flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left",
262
+ !hideEmpty ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50"
263
+ ),
264
+ children: [
265
+ /* @__PURE__ */ jsx(Eye, { className: "h-4 w-4 shrink-0" }),
266
+ "Show empty collections"
267
+ ]
268
+ }
269
+ ),
270
+ /* @__PURE__ */ jsx(HelpIcon, { content: "Include collections with no minted assets yet \u2014 hidden by default to keep the feed clean", side: "left" })
271
+ ] }) })
235
272
  ] })
236
273
  ] }),
237
- /* @__PURE__ */ jsx("div", { className: "px-5 py-3 border-t border-border/60", children: /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: () => setFiltersOpen(false), children: "Apply filters" }) })
274
+ /* @__PURE__ */ jsx("div", { className: "px-5 py-3 border-t border-border/60", children: /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: () => setFiltersOpen(false), children: "Show results" }) })
238
275
  ] }) }),
239
- isInitialLoading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5", children: Array.from({ length: 9 }).map((_, i) => /* @__PURE__ */ jsx(CollectionCardSkeleton, {}, i)) }) : allCollections.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-24 gap-4 text-center", children: [
276
+ isInitialLoading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5", children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(CollectionCardSkeleton, {}, i)) }) : allCollections.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-24 gap-4 text-center", children: [
240
277
  /* @__PURE__ */ jsx(Layers, { className: "h-12 w-12 text-muted-foreground/30" }),
241
278
  /* @__PURE__ */ jsx("p", { className: "text-2xl font-bold", children: "No collections found" }),
242
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground max-w-sm", children: featured ? "No featured collections match the current filters." : hideEmpty ? "No collections with assets yet." : "Deploy the first collection on Medialane." }),
279
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground max-w-sm", children: hideEmpty ? "No collections with assets yet." : "Deploy the first collection on Medialane." }),
243
280
  activeFilters > 0 && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: resetAll, children: "Clear filters" })
244
281
  ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
245
- exclusive && displayCollections.length === 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 gap-3 text-center", children: [
246
- /* @__PURE__ */ jsx(Sparkles, { className: "h-10 w-10 text-muted-foreground/30" }),
247
- /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-muted-foreground", children: "No exclusive collections in this batch" }),
248
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground/70 max-w-xs", children: "Load more collections below, then the filter will catch them." })
249
- ] }),
250
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5", children: displayCollections.map((col) => /* @__PURE__ */ jsx(CollectionCard, { collection: col }, col.contractAddress)) }),
282
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5", children: allCollections.map((col) => /* @__PURE__ */ jsx(CollectionCard, { collection: col }, col.contractAddress)) }),
251
283
  /* @__PURE__ */ jsx(
252
284
  LoadMoreSentinel,
253
285
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collections-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport { Layers, BadgeCheck, Eye, SlidersHorizontal, Award, Sparkles, Package } from \"lucide-react\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.js\";\nimport { HelpIcon } from \"./help-icon.js\";\nimport { PageContainer } from \"./page-container.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { useCollections, type CollectionSort } from \"../utils/use-collections.js\";\nimport { usePlatformStats } from \"../utils/use-platform-stats.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nconst PAGE_SIZE = 18;\n\nconst SORT_OPTIONS: { label: string; value: CollectionSort }[] = [\n { label: \"Recent\", value: \"recent\" },\n { label: \"Most assets\", value: \"supply\" },\n { label: \"Top volume\", value: \"volume\" },\n { label: \"Floor ↑\", value: \"floor\" },\n { label: \"A → Z\", value: \"name\" },\n];\n\nconst SERVICE_TABS = [\n { label: \"All\", value: undefined },\n { label: \"POP Events\", value: \"pop-protocol\" },\n { label: \"Drops\", value: \"drop-collection\" },\n] as const;\n\nexport function CollectionsPage() {\n const { stats } = usePlatformStats();\n const [sort, setSort] = useState<CollectionSort>(\"recent\");\n const [featured, setFeatured] = useState(false);\n const [hideEmpty, setHideEmpty] = useState(true);\n const [exclusive, setExclusive] = useState(false);\n const [service, setService] = useState<string | undefined>(undefined);\n const [filtersOpen, setFiltersOpen] = useState(false);\n const [page, setPage] = useState(1);\n const [allCollections, setAllCollections] = useState<ApiCollection[]>([]);\n\n const { collections, meta, isLoading } = useCollections(\n page,\n PAGE_SIZE,\n featured ? true : undefined,\n sort,\n service === \"pop-protocol\" || service === \"drop-collection\" ? false : hideEmpty,\n service,\n \"ERC721,ERC1155\"\n );\n\n const prevFilters = useRef({ sort, featured, hideEmpty, service });\n useEffect(() => {\n const f = prevFilters.current;\n if (f.sort !== sort || f.featured !== featured || f.hideEmpty !== hideEmpty || f.service !== service) {\n prevFilters.current = { sort, featured, hideEmpty, service };\n setPage(1);\n setAllCollections([]);\n }\n }, [sort, featured, hideEmpty, service]);\n\n useEffect(() => {\n if (isLoading || collections.length === 0) return;\n setAllCollections((prev) => {\n const ids = new Set(prev.map((c) => c.contractAddress));\n const next = collections.filter((c) => !ids.has(c.contractAddress));\n return page === 1 ? collections : [...prev, ...next];\n });\n }, [collections, isLoading, page]);\n\n const hasMore = meta?.total != null ? allCollections.length < meta.total : false;\n const isInitialLoading = isLoading && allCollections.length === 0;\n\n const activeFilters = [sort !== \"recent\", featured, !hideEmpty, exclusive].filter(Boolean).length;\n\n const displayCollections = exclusive\n ? allCollections.filter((c) => !!c.profile?.hasGatedContent)\n : allCollections;\n\n const resetAll = () => {\n setSort(\"recent\");\n setFeatured(false);\n setHideEmpty(true);\n setExclusive(false);\n };\n\n const handleServiceChange = (val: string | undefined) => {\n setService(val);\n };\n\n return (\n <PageContainer className=\"box-border max-w-full pt-14 pb-8 space-y-8\">\n\n <div className=\"space-y-2 pt-8\">\n\n <h1 className=\"text-3xl font-bold\">Onchain Collections</h1>\n <div className=\"flex flex-wrap items-center gap-2 mt-1\">\n {meta?.total != null && (\n <div className=\"flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm\">\n <span className=\"font-bold tabular-nums\">{(meta.total ?? 0).toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Collections</span>\n </div>\n )}\n {stats?.tokens != null && (\n <div className=\"flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm\">\n <span className=\"font-bold tabular-nums\">{stats.tokens.toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Assets</span>\n </div>\n )}\n {stats?.sales != null && (\n <div className=\"flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm\">\n <span className=\"font-bold tabular-nums\">{stats.sales.toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Sales</span>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 pb-3 border-b border-border/60 flex-wrap\">\n <button\n onClick={() => setFiltersOpen(true)}\n className={cn(\n \"relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors\",\n activeFilters > 0 || service !== undefined\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {(activeFilters > 0 || service !== undefined) && (\n <span className=\"flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground\">\n {activeFilters + (service !== undefined ? 1 : 0)}\n </span>\n )}\n </button>\n\n {service !== undefined && (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n {service === \"drop-collection\" ? <Package className=\"h-3 w-3\" /> : <Award className=\"h-3 w-3\" />}\n {SERVICE_TABS.find((t) => t.value === service)?.label}\n <button onClick={() => setService(undefined)} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n )}\n {sort !== \"recent\" && (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n {SORT_OPTIONS.find((o) => o.value === sort)?.label}\n <button onClick={() => setSort(\"recent\")} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n )}\n {featured && (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n <BadgeCheck className=\"h-3 w-3\" />\n Featured\n <button onClick={() => setFeatured(false)} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n )}\n {!hideEmpty && (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n Show empty\n <button onClick={() => setHideEmpty(true)} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n )}\n {exclusive && (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n <Sparkles className=\"h-3 w-3\" />\n Exclusive\n <button onClick={() => setExclusive(false)} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n )}\n </div>\n\n <Dialog open={filtersOpen} onOpenChange={setFiltersOpen}>\n <DialogContent className=\"w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]\">\n\n <div className=\"flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12\">\n <DialogTitle className=\"text-base font-bold flex items-center gap-2\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </DialogTitle>\n {(activeFilters > 0 || service !== undefined) && (\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 text-xs text-muted-foreground\" onClick={() => { resetAll(); setService(undefined); }}>\n Clear all\n </Button>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4 space-y-5\">\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Source</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {SERVICE_TABS.map(({ label, value }) => (\n <button\n key={label}\n onClick={() => handleServiceChange(value)}\n className={cn(\n \"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n service === value\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n {value === \"pop-protocol\" && <Award className=\"h-3 w-3\" />}\n {value === \"drop-collection\" && <Package className=\"h-3 w-3\" />}\n {label}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Sort</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n onClick={() => setSort(opt.value)}\n className={cn(\n \"text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n sort === opt.value\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Show</p>\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setFeatured((v) => !v)}\n className={cn(\n \"flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left\",\n featured\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n <BadgeCheck className=\"h-4 w-4 shrink-0\" />\n Featured only\n </button>\n <HelpIcon content=\"Show only collections featured by Medialane\" side=\"left\" />\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setHideEmpty((v) => !v)}\n className={cn(\n \"flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left\",\n !hideEmpty\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n <Eye className=\"h-4 w-4 shrink-0\" />\n Show empty collections\n </button>\n <HelpIcon content=\"Include collections with no minted assets yet — hidden by default to keep the feed clean\" side=\"left\" />\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setExclusive((v) => !v)}\n className={cn(\n \"flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left\",\n exclusive\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n <Sparkles className=\"h-4 w-4 shrink-0\" />\n Exclusive content only\n </button>\n <HelpIcon content=\"Show only collections with holder-exclusive content\" side=\"left\" />\n </div>\n </div>\n </div>\n\n </div>\n\n <div className=\"px-5 py-3 border-t border-border/60\">\n <Button className=\"w-full\" onClick={() => setFiltersOpen(false)}>\n Apply filters\n </Button>\n </div>\n\n </DialogContent>\n </Dialog>\n\n {isInitialLoading ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5\">\n {Array.from({ length: 9 }).map((_, i) => <CollectionCardSkeleton key={i} />)}\n </div>\n ) : allCollections.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-24 gap-4 text-center\">\n <Layers className=\"h-12 w-12 text-muted-foreground/30\" />\n <p className=\"text-2xl font-bold\">No collections found</p>\n <p className=\"text-muted-foreground max-w-sm\">\n {featured\n ? \"No featured collections match the current filters.\"\n : hideEmpty\n ? \"No collections with assets yet.\"\n : \"Deploy the first collection on Medialane.\"}\n </p>\n {activeFilters > 0 && (\n <Button variant=\"outline\" size=\"sm\" onClick={resetAll}>\n Clear filters\n </Button>\n )}\n </div>\n ) : (\n <div className=\"space-y-6\">\n {exclusive && displayCollections.length === 0 && (\n <div className=\"flex flex-col items-center justify-center py-16 gap-3 text-center\">\n <Sparkles className=\"h-10 w-10 text-muted-foreground/30\" />\n <p className=\"text-base font-semibold text-muted-foreground\">No exclusive collections in this batch</p>\n <p className=\"text-sm text-muted-foreground/70 max-w-xs\">Load more collections below, then the filter will catch them.</p>\n </div>\n )}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5\">\n {displayCollections.map((col) => (\n <CollectionCard key={col.contractAddress} collection={col} />\n ))}\n </div>\n\n <LoadMoreSentinel\n hasMore={hasMore}\n isLoading={isLoading}\n onLoadMore={() => setPage((p) => p + 1)}\n />\n </div>\n )}\n </PageContainer>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/collections-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport useSWR from \"swr\";\nimport { Layers, Eye, SlidersHorizontal, Award, Package, ArrowDownUp, Search, X } from \"lucide-react\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"./select.js\";\nimport { HelpIcon } from \"./help-icon.js\";\nimport { Input } from \"./input.js\";\nimport { ResultThumbnail } from \"./result-thumbnail.js\";\nimport Link from \"./link.js\";\nimport { useRouter } from \"./router.js\";\nimport { PageContainer } from \"./page-container.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { useCollections, type CollectionSort } from \"../utils/use-collections.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { SEARCH_MIN_LENGTH, useSearch } from \"../utils/use-search.js\";\nimport { collectionHref, type ApiCollection } from \"@medialane/sdk\";\n\nconst PAGE_SIZE = 24;\n\nconst SORT_OPTIONS: { label: string; value: CollectionSort }[] = [\n { label: \"Recent\", value: \"recent\" },\n { label: \"Most assets\", value: \"supply\" },\n { label: \"Top volume\", value: \"volume\" },\n { label: \"Floor ↑\", value: \"floor\" },\n { label: \"A → Z\", value: \"name\" },\n];\n\nconst SERVICE_TABS = [\n { label: \"All\", value: undefined },\n { label: \"POP Events\", value: \"pop-protocol\" },\n { label: \"Drops\", value: \"drop-collection\" },\n] as const;\n\nfunction useStandardCounts() {\n const client = useMedialaneClient();\n const { data } = useSWR(\n \"collection-standard-counts\",\n async () => {\n const [single, multi] = await Promise.all(\n [\"ERC721\", \"ERC1155\"].map((standard) =>\n client.api.collections.list({ standard, hideEmpty: true, limit: 1 })\n )\n );\n return { single: single.meta?.total, multi: multi.meta?.total };\n },\n { revalidateOnFocus: false, dedupingInterval: 60_000 }\n );\n return data ?? null;\n}\n\nfunction CollectionSearch() {\n const router = useRouter();\n const [value, setValue] = useState(\"\");\n const [query, setQuery] = useState(\"\");\n const [open, setOpen] = useState(false);\n const { results } = useSearch(query, { limit: 6 });\n const matches = results?.collections ?? [];\n\n useEffect(() => {\n const timer = setTimeout(() => setQuery(value), 300);\n return () => clearTimeout(timer);\n }, [value]);\n\n const clear = () => {\n setValue(\"\");\n setQuery(\"\");\n setOpen(false);\n };\n\n return (\n <div className=\"relative w-full sm:max-w-sm sm:flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n className=\"h-9 pl-9 pr-9 text-sm\"\n placeholder=\"Search collections\"\n aria-label=\"Search collections\"\n value={value}\n onChange={(e) => { setValue(e.target.value); setOpen(true); }}\n onFocus={() => setOpen(true)}\n onBlur={() => setTimeout(() => setOpen(false), 200)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && value.trim().length >= SEARCH_MIN_LENGTH) {\n router.push(`/search?q=${encodeURIComponent(value.trim())}`);\n }\n }}\n />\n {value && (\n <button\n aria-label=\"Clear search\"\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n onClick={clear}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {open && matches.length > 0 && (\n <div className=\"absolute left-0 right-0 top-full z-50 mt-1 overflow-hidden rounded-xl border border-border bg-popover shadow-lg\">\n {matches.map((col) => {\n const label = col.name ?? \"Untitled\";\n return (\n <Link\n key={col.contractAddress}\n href={collectionHref(\"STARKNET\", col.contractAddress)}\n className=\"flex items-center gap-3 px-3 py-2.5 transition-colors hover:bg-muted/50\"\n >\n <ResultThumbnail src={col.image} label={label} />\n <span className=\"truncate text-sm font-medium\">{label}</span>\n </Link>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nexport function CollectionsPage() {\n const counts = useStandardCounts();\n const [sort, setSort] = useState<CollectionSort>(\"recent\");\n const [hideEmpty, setHideEmpty] = useState(true);\n const [service, setService] = useState<string | undefined>(undefined);\n const [filtersOpen, setFiltersOpen] = useState(false);\n const [page, setPage] = useState(1);\n const [allCollections, setAllCollections] = useState<ApiCollection[]>([]);\n\n const { collections, meta, isLoading } = useCollections(\n page,\n PAGE_SIZE,\n undefined,\n sort,\n service === \"pop-protocol\" || service === \"drop-collection\" ? false : hideEmpty,\n service,\n \"ERC721,ERC1155\"\n );\n\n const { collections: featuredCollections, isLoading: featuredLoading } = useCollections(\n 1,\n 8,\n true,\n \"recent\",\n true,\n undefined,\n \"ERC721,ERC1155\"\n );\n\n const prevFilters = useRef({ sort, hideEmpty, service });\n useEffect(() => {\n const f = prevFilters.current;\n if (f.sort !== sort || f.hideEmpty !== hideEmpty || f.service !== service) {\n prevFilters.current = { sort, hideEmpty, service };\n setPage(1);\n setAllCollections([]);\n }\n }, [sort, hideEmpty, service]);\n\n useEffect(() => {\n if (isLoading || collections.length === 0) return;\n setAllCollections((prev) => {\n const ids = new Set(prev.map((c) => c.contractAddress));\n const next = collections.filter((c) => !ids.has(c.contractAddress));\n return page === 1 ? collections : [...prev, ...next];\n });\n }, [collections, isLoading, page]);\n\n const hasMore = meta?.total != null ? allCollections.length < meta.total : false;\n const isInitialLoading = isLoading && allCollections.length === 0;\n\n const activeFilters = [!hideEmpty].filter(Boolean).length;\n\n const resetAll = () => {\n setHideEmpty(true);\n };\n\n const handleServiceChange = (val: string | undefined) => {\n setService(val);\n };\n\n return (\n <PageContainer className=\"box-border max-w-full pt-14 pb-8 space-y-8\">\n\n <div className=\"space-y-2 pt-8\">\n\n <h1 className=\"text-3xl font-bold\">Onchain Collections</h1>\n <div className=\"flex flex-wrap items-center gap-2 mt-1\">\n {counts?.single != null && (\n <div className=\"flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm\">\n <span className=\"font-bold tabular-nums\">{counts.single.toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Single NFTs</span>\n </div>\n )}\n {counts?.multi != null && (\n <div className=\"flex items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-sm\">\n <span className=\"font-bold tabular-nums\">{counts.multi.toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Multi editions</span>\n </div>\n )}\n </div>\n </div>\n\n {(featuredLoading || featuredCollections.length > 0) && (\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5\">\n {featuredLoading\n ? Array.from({ length: 8 }).map((_, i) => <CollectionCardSkeleton key={i} />)\n : featuredCollections.map((col) => (\n <CollectionCard key={col.contractAddress} collection={col} />\n ))}\n </div>\n )}\n\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <CollectionSearch />\n <div className=\"flex items-center gap-2 sm:ml-auto\">\n <Select value={sort} onValueChange={(v) => setSort(v as CollectionSort)}>\n <SelectTrigger className=\"h-9 w-auto gap-1.5 text-xs\" aria-label=\"Sort collections\">\n <ArrowDownUp className=\"h-3.5 w-3.5 text-muted-foreground\" />\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SORT_OPTIONS.map((opt) => (\n <SelectItem key={opt.value} value={opt.value} className=\"text-xs\">{opt.label}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n <button\n onClick={() => setFiltersOpen(true)}\n className={cn(\n \"relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors\",\n activeFilters > 0 || service !== undefined\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {(activeFilters > 0 || service !== undefined) && (\n <span className=\"flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground\">\n {activeFilters + (service !== undefined ? 1 : 0)}\n </span>\n )}\n </button>\n </div>\n </div>\n {(service !== undefined || !hideEmpty) && (\n <div className=\"flex flex-wrap items-center gap-2\">\n {service !== undefined && (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n {service === \"drop-collection\" ? <Package className=\"h-3 w-3\" /> : <Award className=\"h-3 w-3\" />}\n {SERVICE_TABS.find((t) => t.value === service)?.label}\n <button onClick={() => setService(undefined)} className=\"ml-0.5 hover:text-primary/60\" aria-label=\"Remove source filter\">×</button>\n </span>\n )}\n {!hideEmpty && (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n Show empty\n <button onClick={() => setHideEmpty(true)} className=\"ml-0.5 hover:text-primary/60\" aria-label=\"Remove show empty filter\">×</button>\n </span>\n )}\n </div>\n )}\n </div>\n\n <Dialog open={filtersOpen} onOpenChange={setFiltersOpen}>\n <DialogContent className=\"w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]\">\n\n <div className=\"flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12\">\n <DialogTitle className=\"text-base font-bold flex items-center gap-2\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </DialogTitle>\n {(activeFilters > 0 || service !== undefined) && (\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 text-xs text-muted-foreground\" onClick={() => { resetAll(); setService(undefined); }}>\n Clear all\n </Button>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4 space-y-5\">\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Source</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {SERVICE_TABS.map(({ label, value }) => (\n <button\n key={label}\n onClick={() => handleServiceChange(value)}\n className={cn(\n \"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n service === value\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n {value === \"pop-protocol\" && <Award className=\"h-3 w-3\" />}\n {value === \"drop-collection\" && <Package className=\"h-3 w-3\" />}\n {label}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Show</p>\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setHideEmpty((v) => !v)}\n className={cn(\n \"flex flex-1 items-center gap-2 text-sm px-3 py-2 rounded-lg border transition-colors text-left\",\n !hideEmpty\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n <Eye className=\"h-4 w-4 shrink-0\" />\n Show empty collections\n </button>\n <HelpIcon content=\"Include collections with no minted assets yet — hidden by default to keep the feed clean\" side=\"left\" />\n </div>\n </div>\n </div>\n\n </div>\n\n <div className=\"px-5 py-3 border-t border-border/60\">\n <Button className=\"w-full\" onClick={() => setFiltersOpen(false)}>\n Show results\n </Button>\n </div>\n\n </DialogContent>\n </Dialog>\n\n {isInitialLoading ? (\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5\">\n {Array.from({ length: 8 }).map((_, i) => <CollectionCardSkeleton key={i} />)}\n </div>\n ) : allCollections.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-24 gap-4 text-center\">\n <Layers className=\"h-12 w-12 text-muted-foreground/30\" />\n <p className=\"text-2xl font-bold\">No collections found</p>\n <p className=\"text-muted-foreground max-w-sm\">\n {hideEmpty\n ? \"No collections with assets yet.\"\n : \"Deploy the first collection on Medialane.\"}\n </p>\n {activeFilters > 0 && (\n <Button variant=\"outline\" size=\"sm\" onClick={resetAll}>\n Clear filters\n </Button>\n )}\n </div>\n ) : (\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-5\">\n {allCollections.map((col) => (\n <CollectionCard key={col.contractAddress} collection={col} />\n ))}\n </div>\n\n <LoadMoreSentinel\n hasMore={hasMore}\n isLoading={isLoading}\n onLoadMore={() => setPage((p) => p + 1)}\n />\n </div>\n )}\n </PageContainer>\n );\n}\n"],"mappings":"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","names":[]}
@@ -175,7 +175,7 @@ function CreatorPage({ address: addressProp, username, useAssetActions, useMarke
175
175
  `${t.contractAddress}-${t.tokenId}`
176
176
  );
177
177
  }) })),
178
- activeTab === "collections" && (collectionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCardSkeleton, {}, i)) }) : collections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.LayoutGrid, heading: "No collections yet", body: "Collections this creator makes appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: collections.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCard, { collection: c }, c.contractAddress)) })),
178
+ activeTab === "collections" && (collectionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCardSkeleton, {}, i)) }) : collections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.LayoutGrid, heading: "No collections yet", body: "Collections this creator makes appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-3", children: collections.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_collection_card.CollectionCard, { collection: c }, c.contractAddress)) })),
179
179
  activeTab === "listings" && (ordersLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card.ListingCardSkeleton, {}, i)) }) : listings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.ShoppingBag, heading: "No active listings", body: "Works this creator lists for sale appear here." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: GRID, children: listings.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card.ListingCard, { order: o, onBuy: isOwner ? void 0 : market.buy, usdValue: (0, import_order_usd.orderUsd)(o.price, prices), overflowMenu: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_listing_card_menu.ListingCardMenu, { order: o }) }, o.orderHash)) })),
180
180
  activeTab === "analytics" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "max-w-2xl", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-64 w-full animate-pulse rounded-xl bg-muted/40" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CreatorAnalytics, { activities, isLoading: activitiesLoading }) }) }),
181
181
  activeTab === "activity" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "max-w-2xl", children: activitiesLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-5", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_skeleton.Skeleton, { className: "h-10 w-full rounded-xl" }, i)) }) : activities.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tab_empty_state.TabEmptyState, { icon: import_lucide_react.Activity, heading: "No activity yet", body: "This creator's activity appears here as it happens." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: activities.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_activity_timeline_row.ActivityTimelineRow, { event: a, isLast: i === activities.length - 1 }, i)) }) })