@medialane/ui 0.322.0 → 0.323.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.
- package/dist/components/collection-card.cjs +2 -3
- package/dist/components/collection-card.cjs.map +1 -1
- package/dist/components/collection-card.js +2 -3
- package/dist/components/collection-card.js.map +1 -1
- package/dist/components/collections-page.cjs +32 -26
- package/dist/components/collections-page.cjs.map +1 -1
- package/dist/components/collections-page.js +33 -27
- package/dist/components/collections-page.js.map +1 -1
- package/dist/components/discover-collections-strip.cjs +3 -2
- package/dist/components/discover-collections-strip.cjs.map +1 -1
- package/dist/components/discover-collections-strip.d.cts +2 -1
- package/dist/components/discover-collections-strip.d.ts +2 -1
- package/dist/components/discover-collections-strip.js +3 -2
- package/dist/components/discover-collections-strip.js.map +1 -1
- package/dist/components/discover-page.cjs +3 -1
- package/dist/components/discover-page.cjs.map +1 -1
- package/dist/components/discover-page.js +3 -1
- package/dist/components/discover-page.js.map +1 -1
- package/dist/components/featured-carousel.cjs +3 -21
- package/dist/components/featured-carousel.cjs.map +1 -1
- package/dist/components/featured-carousel.js +3 -21
- package/dist/components/featured-carousel.js.map +1 -1
- package/dist/components/ip-type-nav.cjs +2 -14
- package/dist/components/ip-type-nav.cjs.map +1 -1
- package/dist/components/ip-type-nav.js +3 -15
- package/dist/components/ip-type-nav.js.map +1 -1
- package/package.json +1 -1
|
@@ -58,7 +58,7 @@ function CollectionCard({ collection, href, settingsHref, className }) {
|
|
|
58
58
|
const imageUrl = resolveImage(collection.image, { width: 480 });
|
|
59
59
|
const showImage = imageUrl && !imgError;
|
|
60
60
|
const floor = parseFloorPrice(collection.floorPrice);
|
|
61
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_motion_primitives.MotionCard, { className: (0, import_cn.cn)("
|
|
61
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_motion_primitives.MotionCard, { className: (0, import_cn.cn)("group relative overflow-hidden rounded-[calc(var(--radius)*1.25)]", className), children: [
|
|
62
62
|
settingsHref && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
63
63
|
import_link.default,
|
|
64
64
|
{
|
|
@@ -89,7 +89,6 @@ function CollectionCard({ collection, href, settingsHref, className }) {
|
|
|
89
89
|
(collection.totalSupply != null || floor) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
90
90
|
collection.totalSupply != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rounded-full px-2.5 py-1 text-2xs font-medium text-white bg-black/30 backdrop-blur-md", children: collection.totalSupply.toLocaleString() }),
|
|
91
91
|
floor && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-2xs font-bold text-white bg-black/30 backdrop-blur-md", children: [
|
|
92
|
-
"Floor",
|
|
93
92
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_currency_icon.CurrencyIcon, { symbol: floor.symbol, size: 10 }),
|
|
94
93
|
floor.amount
|
|
95
94
|
] })
|
|
@@ -99,7 +98,7 @@ function CollectionCard({ collection, href, settingsHref, className }) {
|
|
|
99
98
|
] });
|
|
100
99
|
}
|
|
101
100
|
function CollectionCardSkeleton() {
|
|
102
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "
|
|
101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-hidden rounded-[calc(var(--radius)*1.25)]", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative aspect-square w-full overflow-hidden bg-muted", children: [
|
|
103
102
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 animate-pulse bg-muted" }),
|
|
104
103
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "absolute bottom-3 left-3 right-3 space-y-1.5", children: [
|
|
105
104
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse" }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n\n href?: string;\n\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const resolveImage = useImageUrl();\n const [imgError, setImgError] = useState(false);\n const imageUrl = resolveImage(collection.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const floor = parseFloorPrice(collection.floorPrice);\n\n return (\n <MotionCard className={cn(\"
|
|
1
|
+
{"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n\n href?: string;\n\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const resolveImage = useImageUrl();\n const [imgError, setImgError] = useState(false);\n const imageUrl = resolveImage(collection.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const floor = parseFloorPrice(collection.floorPrice);\n\n return (\n <MotionCard className={cn(\"group relative overflow-hidden rounded-[calc(var(--radius)*1.25)]\", className)}>\n {settingsHref && (\n <Link\n href={settingsHref}\n onClick={(e) => e.stopPropagation()}\n className=\"absolute top-2 right-2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center\"\n aria-label=\"Collection settings\"\n >\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-black/40 backdrop-blur-sm text-white/70 hover:text-white hover:bg-black/60 transition-colors\">\n <Settings2 className=\"h-3.5 w-3.5\" />\n </span>\n </Link>\n )}\n\n <Link href={href ?? `/collections/${collection.contractAddress}`} className=\"block relative h-full\">\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={collection.name ?? \"Collection\"}\n fill\n className=\"object-cover card-media\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n\n <div className=\"absolute inset-x-2 bottom-2 flex flex-col items-start gap-1\">\n {!collection.name && collection.metadataStatus === \"PENDING\" ? (\n <span className=\"flex items-center gap-1 rounded-full px-2.5 py-1 text-2xs text-white bg-black/30 backdrop-blur-md\">\n <Loader2 className=\"h-2.5 w-2.5 animate-spin\" />\n Indexing…\n </span>\n ) : (\n <p className=\"max-w-full truncate rounded-full px-3 py-1 text-sm font-bold leading-tight text-white bg-black/30 backdrop-blur-md\">\n {collection.name ?? \"Unnamed Collection\"}\n </p>\n )}\n\n {(collection.totalSupply != null || floor) && (\n <div className=\"flex flex-wrap items-center gap-1\">\n {collection.totalSupply != null && (\n <span className=\"rounded-full px-2.5 py-1 text-2xs font-medium text-white bg-black/30 backdrop-blur-md\">\n {collection.totalSupply.toLocaleString()}\n </span>\n )}\n {floor && (\n <span className=\"inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-2xs font-bold text-white bg-black/30 backdrop-blur-md\">\n <CurrencyIcon symbol={floor.symbol} size={10} />\n {floor.amount}\n </span>\n )}\n </div>\n )}\n </div>\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function CollectionCardSkeleton() {\n return (\n <div className=\"overflow-hidden rounded-[calc(var(--radius)*1.25)]\">\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n <div className=\"absolute inset-0 animate-pulse bg-muted\" />\n <div className=\"absolute bottom-3 left-3 right-3 space-y-1.5\">\n <div className=\"h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-1/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiDY;AA/CZ,mBAAyB;AACzB,kBAAiB;AACjB,mBAAkB;AAClB,0BAAmC;AACnC,gBAAmB;AACnB,oBAAmC;AACnC,+BAA2B;AAC3B,2BAA6B;AAE7B,2BAA4B;AAE5B,SAAS,gBAAgB,OAA6E;AACpG,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,gBAAY,kCAAmB,KAAK;AAC1C,QAAM,QAAQ,UAAU,KAAK,EAAE,MAAM,KAAK;AAC1C,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,QAAM,SAAS,MAAM,MAAM,SAAS,CAAC;AACrC,QAAM,SAAS,MAAM,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG;AAC1C,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAWO,SAAS,eAAe,EAAE,YAAY,MAAM,cAAc,UAAU,GAAwB;AACjG,QAAM,mBAAe,kCAAY;AACjC,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAS,KAAK;AAC9C,QAAM,WAAW,aAAa,WAAW,OAAO,EAAE,OAAO,IAAI,CAAC;AAC9D,QAAM,YAAY,YAAY,CAAC;AAC/B,QAAM,QAAQ,gBAAgB,WAAW,UAAU;AAEnD,SACE,6CAAC,uCAAW,eAAW,cAAG,qEAAqE,SAAS,GACrG;AAAA,oBACC;AAAA,MAAC,YAAAA;AAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,QAClC,WAAU;AAAA,QACV,cAAW;AAAA,QAEX,sDAAC,UAAK,WAAU,yJACd,sDAAC,iCAAU,WAAU,eAAc,GACrC;AAAA;AAAA,IACF;AAAA,IAGF,4CAAC,YAAAA,SAAA,EAAK,MAAM,QAAQ,gBAAgB,WAAW,eAAe,IAAI,WAAU,yBAC1E,uDAAC,SAAI,WAAU,0DACZ;AAAA,kBACC;AAAA,QAAC,aAAAC;AAAA,QAAA;AAAA,UACC,KAAK;AAAA,UACL,KAAK,WAAW,QAAQ;AAAA,UACxB,MAAI;AAAA,UACJ,WAAU;AAAA,UACV,SAAS,MAAM,YAAY,IAAI;AAAA,UAC/B,aAAW;AAAA;AAAA,MACb,IAEA,4CAAC,SAAI,WAAU,8FAA6F;AAAA,MAG9G,6CAAC,SAAI,WAAU,+DACZ;AAAA,SAAC,WAAW,QAAQ,WAAW,mBAAmB,YACjD,6CAAC,UAAK,WAAU,qGACd;AAAA,sDAAC,+BAAQ,WAAU,4BAA2B;AAAA,UAAE;AAAA,WAElD,IAEA,4CAAC,OAAE,WAAU,sHACV,qBAAW,QAAQ,sBACtB;AAAA,SAGA,WAAW,eAAe,QAAQ,UAClC,6CAAC,SAAI,WAAU,qCACZ;AAAA,qBAAW,eAAe,QACzB,4CAAC,UAAK,WAAU,yFACb,qBAAW,YAAY,eAAe,GACzC;AAAA,UAED,SACC,6CAAC,UAAK,WAAU,sHACd;AAAA,wDAAC,qCAAa,QAAQ,MAAM,QAAQ,MAAM,IAAI;AAAA,YAC7C,MAAM;AAAA,aACT;AAAA,WAEJ;AAAA,SAEJ;AAAA,OACF,GACF;AAAA,KACF;AAEJ;AAEO,SAAS,yBAAyB;AACvC,SACE,4CAAC,SAAI,WAAU,sDACb,uDAAC,SAAI,WAAU,0DACb;AAAA,gDAAC,SAAI,WAAU,2CAA0C;AAAA,IACzD,6CAAC,SAAI,WAAU,gDACb;AAAA,kDAAC,SAAI,WAAU,6DAA4D;AAAA,MAC3E,4CAAC,SAAI,WAAU,6DAA4D;AAAA,OAC7E;AAAA,KACF,GACF;AAEJ;","names":["Link","Image"]}
|
|
@@ -24,7 +24,7 @@ function CollectionCard({ collection, href, settingsHref, className }) {
|
|
|
24
24
|
const imageUrl = resolveImage(collection.image, { width: 480 });
|
|
25
25
|
const showImage = imageUrl && !imgError;
|
|
26
26
|
const floor = parseFloorPrice(collection.floorPrice);
|
|
27
|
-
return /* @__PURE__ */ jsxs(MotionCard, { className: cn("
|
|
27
|
+
return /* @__PURE__ */ jsxs(MotionCard, { className: cn("group relative overflow-hidden rounded-[calc(var(--radius)*1.25)]", className), children: [
|
|
28
28
|
settingsHref && /* @__PURE__ */ jsx(
|
|
29
29
|
Link,
|
|
30
30
|
{
|
|
@@ -55,7 +55,6 @@ function CollectionCard({ collection, href, settingsHref, className }) {
|
|
|
55
55
|
(collection.totalSupply != null || floor) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
56
56
|
collection.totalSupply != null && /* @__PURE__ */ jsx("span", { className: "rounded-full px-2.5 py-1 text-2xs font-medium text-white bg-black/30 backdrop-blur-md", children: collection.totalSupply.toLocaleString() }),
|
|
57
57
|
floor && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-2xs font-bold text-white bg-black/30 backdrop-blur-md", children: [
|
|
58
|
-
"Floor",
|
|
59
58
|
/* @__PURE__ */ jsx(CurrencyIcon, { symbol: floor.symbol, size: 10 }),
|
|
60
59
|
floor.amount
|
|
61
60
|
] })
|
|
@@ -65,7 +64,7 @@ function CollectionCard({ collection, href, settingsHref, className }) {
|
|
|
65
64
|
] });
|
|
66
65
|
}
|
|
67
66
|
function CollectionCardSkeleton() {
|
|
68
|
-
return /* @__PURE__ */ jsx("div", { className: "
|
|
67
|
+
return /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-[calc(var(--radius)*1.25)]", children: /* @__PURE__ */ jsxs("div", { className: "relative aspect-square w-full overflow-hidden bg-muted", children: [
|
|
69
68
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 animate-pulse bg-muted" }),
|
|
70
69
|
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 left-3 right-3 space-y-1.5", children: [
|
|
71
70
|
/* @__PURE__ */ jsx("div", { className: "h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse" }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n\n href?: string;\n\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const resolveImage = useImageUrl();\n const [imgError, setImgError] = useState(false);\n const imageUrl = resolveImage(collection.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const floor = parseFloorPrice(collection.floorPrice);\n\n return (\n <MotionCard className={cn(\"
|
|
1
|
+
{"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n\n href?: string;\n\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const resolveImage = useImageUrl();\n const [imgError, setImgError] = useState(false);\n const imageUrl = resolveImage(collection.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const floor = parseFloorPrice(collection.floorPrice);\n\n return (\n <MotionCard className={cn(\"group relative overflow-hidden rounded-[calc(var(--radius)*1.25)]\", className)}>\n {settingsHref && (\n <Link\n href={settingsHref}\n onClick={(e) => e.stopPropagation()}\n className=\"absolute top-2 right-2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center\"\n aria-label=\"Collection settings\"\n >\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-black/40 backdrop-blur-sm text-white/70 hover:text-white hover:bg-black/60 transition-colors\">\n <Settings2 className=\"h-3.5 w-3.5\" />\n </span>\n </Link>\n )}\n\n <Link href={href ?? `/collections/${collection.contractAddress}`} className=\"block relative h-full\">\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={collection.name ?? \"Collection\"}\n fill\n className=\"object-cover card-media\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n\n <div className=\"absolute inset-x-2 bottom-2 flex flex-col items-start gap-1\">\n {!collection.name && collection.metadataStatus === \"PENDING\" ? (\n <span className=\"flex items-center gap-1 rounded-full px-2.5 py-1 text-2xs text-white bg-black/30 backdrop-blur-md\">\n <Loader2 className=\"h-2.5 w-2.5 animate-spin\" />\n Indexing…\n </span>\n ) : (\n <p className=\"max-w-full truncate rounded-full px-3 py-1 text-sm font-bold leading-tight text-white bg-black/30 backdrop-blur-md\">\n {collection.name ?? \"Unnamed Collection\"}\n </p>\n )}\n\n {(collection.totalSupply != null || floor) && (\n <div className=\"flex flex-wrap items-center gap-1\">\n {collection.totalSupply != null && (\n <span className=\"rounded-full px-2.5 py-1 text-2xs font-medium text-white bg-black/30 backdrop-blur-md\">\n {collection.totalSupply.toLocaleString()}\n </span>\n )}\n {floor && (\n <span className=\"inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-2xs font-bold text-white bg-black/30 backdrop-blur-md\">\n <CurrencyIcon symbol={floor.symbol} size={10} />\n {floor.amount}\n </span>\n )}\n </div>\n )}\n </div>\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function CollectionCardSkeleton() {\n return (\n <div className=\"overflow-hidden rounded-[calc(var(--radius)*1.25)]\">\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n <div className=\"absolute inset-0 animate-pulse bg-muted\" />\n <div className=\"absolute bottom-3 left-3 right-3 space-y-1.5\">\n <div className=\"h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-1/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";AAiDY,cAsBE,YAtBF;AA/CZ,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,SAAS,SAAS,iBAAiB;AACnC,SAAS,UAAU;AACnB,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAE5B,SAAS,gBAAgB,OAA6E;AACpG,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,YAAY,mBAAmB,KAAK;AAC1C,QAAM,QAAQ,UAAU,KAAK,EAAE,MAAM,KAAK;AAC1C,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,QAAM,SAAS,MAAM,MAAM,SAAS,CAAC;AACrC,QAAM,SAAS,MAAM,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG;AAC1C,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAWO,SAAS,eAAe,EAAE,YAAY,MAAM,cAAc,UAAU,GAAwB;AACjG,QAAM,eAAe,YAAY;AACjC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,WAAW,aAAa,WAAW,OAAO,EAAE,OAAO,IAAI,CAAC;AAC9D,QAAM,YAAY,YAAY,CAAC;AAC/B,QAAM,QAAQ,gBAAgB,WAAW,UAAU;AAEnD,SACE,qBAAC,cAAW,WAAW,GAAG,qEAAqE,SAAS,GACrG;AAAA,oBACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,QAClC,WAAU;AAAA,QACV,cAAW;AAAA,QAEX,8BAAC,UAAK,WAAU,yJACd,8BAAC,aAAU,WAAU,eAAc,GACrC;AAAA;AAAA,IACF;AAAA,IAGF,oBAAC,QAAK,MAAM,QAAQ,gBAAgB,WAAW,eAAe,IAAI,WAAU,yBAC1E,+BAAC,SAAI,WAAU,0DACZ;AAAA,kBACC;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,KAAK,WAAW,QAAQ;AAAA,UACxB,MAAI;AAAA,UACJ,WAAU;AAAA,UACV,SAAS,MAAM,YAAY,IAAI;AAAA,UAC/B,aAAW;AAAA;AAAA,MACb,IAEA,oBAAC,SAAI,WAAU,8FAA6F;AAAA,MAG9G,qBAAC,SAAI,WAAU,+DACZ;AAAA,SAAC,WAAW,QAAQ,WAAW,mBAAmB,YACjD,qBAAC,UAAK,WAAU,qGACd;AAAA,8BAAC,WAAQ,WAAU,4BAA2B;AAAA,UAAE;AAAA,WAElD,IAEA,oBAAC,OAAE,WAAU,sHACV,qBAAW,QAAQ,sBACtB;AAAA,SAGA,WAAW,eAAe,QAAQ,UAClC,qBAAC,SAAI,WAAU,qCACZ;AAAA,qBAAW,eAAe,QACzB,oBAAC,UAAK,WAAU,yFACb,qBAAW,YAAY,eAAe,GACzC;AAAA,UAED,SACC,qBAAC,UAAK,WAAU,sHACd;AAAA,gCAAC,gBAAa,QAAQ,MAAM,QAAQ,MAAM,IAAI;AAAA,YAC7C,MAAM;AAAA,aACT;AAAA,WAEJ;AAAA,SAEJ;AAAA,OACF,GACF;AAAA,KACF;AAEJ;AAEO,SAAS,yBAAyB;AACvC,SACE,oBAAC,SAAI,WAAU,sDACb,+BAAC,SAAI,WAAU,0DACb;AAAA,wBAAC,SAAI,WAAU,2CAA0C;AAAA,IACzD,qBAAC,SAAI,WAAU,gDACb;AAAA,0BAAC,SAAI,WAAU,6DAA4D;AAAA,MAC3E,oBAAC,SAAI,WAAU,6DAA4D;AAAA,OAC7E;AAAA,KACF,GACF;AAEJ;","names":[]}
|
|
@@ -38,7 +38,6 @@ var import_swr = __toESM(require("swr"), 1);
|
|
|
38
38
|
var import_lucide_react = require("lucide-react");
|
|
39
39
|
var import_button = require("./button.js");
|
|
40
40
|
var import_dialog = require("./dialog.js");
|
|
41
|
-
var import_select = require("./select.js");
|
|
42
41
|
var import_help_icon = require("./help-icon.js");
|
|
43
42
|
var import_input = require("./input.js");
|
|
44
43
|
var import_result_thumbnail = require("./result-thumbnail.js");
|
|
@@ -190,9 +189,10 @@ function CollectionsPage() {
|
|
|
190
189
|
}, [collections, isLoading, page]);
|
|
191
190
|
const hasMore = meta?.total != null ? allCollections.length < meta.total : false;
|
|
192
191
|
const isInitialLoading = isLoading && allCollections.length === 0;
|
|
193
|
-
const activeFilters = [!hideEmpty].filter(Boolean).length;
|
|
192
|
+
const activeFilters = [!hideEmpty, sort !== "recent"].filter(Boolean).length;
|
|
194
193
|
const resetAll = () => {
|
|
195
194
|
setHideEmpty(true);
|
|
195
|
+
setSort("recent");
|
|
196
196
|
};
|
|
197
197
|
const handleServiceChange = (val) => {
|
|
198
198
|
setService(val);
|
|
@@ -215,30 +215,21 @@ function CollectionsPage() {
|
|
|
215
215
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-3", children: [
|
|
216
216
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
217
217
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CollectionSearch, {}),
|
|
218
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
),
|
|
234
|
-
children: [
|
|
235
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.SlidersHorizontal, { className: "h-3.5 w-3.5" }),
|
|
236
|
-
"Filters",
|
|
237
|
-
(activeFilters > 0 || service !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime.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) })
|
|
238
|
-
]
|
|
239
|
-
}
|
|
240
|
-
)
|
|
241
|
-
] })
|
|
218
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-center gap-2 sm:ml-auto", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
219
|
+
"button",
|
|
220
|
+
{
|
|
221
|
+
onClick: () => setFiltersOpen(true),
|
|
222
|
+
"aria-label": "Filters",
|
|
223
|
+
className: (0, import_cn.cn)(
|
|
224
|
+
"relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors",
|
|
225
|
+
activeFilters > 0 || service !== void 0 ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
|
|
226
|
+
),
|
|
227
|
+
children: [
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.SlidersHorizontal, { className: "h-3.5 w-3.5" }),
|
|
229
|
+
(activeFilters > 0 || service !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime.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) })
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
) })
|
|
242
233
|
] }),
|
|
243
234
|
(service !== void 0 || !hideEmpty) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
244
235
|
service !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.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: [
|
|
@@ -264,6 +255,21 @@ function CollectionsPage() {
|
|
|
264
255
|
}, children: "Clear all" })
|
|
265
256
|
] }),
|
|
266
257
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 overflow-y-auto px-5 py-4 space-y-5", children: [
|
|
258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
259
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs font-semibold text-muted-foreground", children: "Sort by" }),
|
|
260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-1.5", children: SORT_OPTIONS.map(({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
261
|
+
"button",
|
|
262
|
+
{
|
|
263
|
+
onClick: () => setSort(value),
|
|
264
|
+
className: (0, import_cn.cn)(
|
|
265
|
+
"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap",
|
|
266
|
+
sort === value ? "border-primary bg-primary/10 text-primary font-medium" : "border-border text-muted-foreground hover:border-primary/50"
|
|
267
|
+
),
|
|
268
|
+
children: label
|
|
269
|
+
},
|
|
270
|
+
value
|
|
271
|
+
)) })
|
|
272
|
+
] }),
|
|
267
273
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
268
274
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs font-semibold text-muted-foreground", children: "Source" }),
|
|
269
275
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-1.5", children: SERVICE_TABS.map(({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -1 +1 @@
|
|
|
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 = 25;\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 10,\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 && 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.single + counts.multi).toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Collections</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-5 gap-4 sm:gap-5\">\n {featuredLoading\n ? Array.from({ length: 10 }).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-5 gap-4 sm:gap-5\">\n {Array.from({ length: 10 }).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-5 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4EM;AA1EN,mBAA4C;AAC5C,iBAAmB;AACnB,0BAAuF;AACvF,oBAAuB;AACvB,oBAAmD;AACnD,oBAA8E;AAC9E,uBAAyB;AACzB,mBAAsB;AACtB,8BAAgC;AAChC,kBAAiB;AACjB,oBAA0B;AAC1B,4BAA8B;AAC9B,6BAAuD;AACvD,gCAAiC;AACjC,gBAAmB;AACnB,6BAAoD;AACpD,kCAAmC;AACnC,wBAA6C;AAC7C,iBAAmD;AAEnD,MAAM,YAAY;AAElB,MAAM,eAA2D;AAAA,EAC/D,EAAE,OAAO,UAAe,OAAO,SAAU;AAAA,EACzC,EAAE,OAAO,eAAe,OAAO,SAAU;AAAA,EACzC,EAAE,OAAO,cAAe,OAAO,SAAU;AAAA,EACzC,EAAE,OAAO,gBAAc,OAAO,QAAU;AAAA,EACxC,EAAE,OAAO,cAAc,OAAO,OAAU;AAC1C;AAEA,MAAM,eAAe;AAAA,EACnB,EAAE,OAAO,OAAc,OAAO,OAAa;AAAA,EAC3C,EAAE,OAAO,cAAc,OAAO,eAAe;AAAA,EAC7C,EAAE,OAAO,SAAc,OAAO,kBAAkB;AAClD;AAEA,SAAS,oBAAoB;AAC3B,QAAM,aAAS,gDAAmB;AAClC,QAAM,EAAE,KAAK,QAAI,WAAAA;AAAA,IACf;AAAA,IACA,YAAY;AACV,YAAM,CAAC,QAAQ,KAAK,IAAI,MAAM,QAAQ;AAAA,QACpC,CAAC,UAAU,SAAS,EAAE;AAAA,UAAI,CAAC,aACzB,OAAO,IAAI,YAAY,KAAK,EAAE,UAAU,WAAW,MAAM,OAAO,EAAE,CAAC;AAAA,QACrE;AAAA,MACF;AACA,aAAO,EAAE,QAAQ,OAAO,MAAM,OAAO,OAAO,MAAM,MAAM,MAAM;AAAA,IAChE;AAAA,IACA,EAAE,mBAAmB,OAAO,kBAAkB,IAAO;AAAA,EACvD;AACA,SAAO,QAAQ;AACjB;AAEA,SAAS,mBAAmB;AAC1B,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,EAAE,QAAQ,QAAI,6BAAU,OAAO,EAAE,OAAO,EAAE,CAAC;AACjD,QAAM,UAAU,SAAS,eAAe,CAAC;AAEzC,8BAAU,MAAM;AACd,UAAM,QAAQ,WAAW,MAAM,SAAS,KAAK,GAAG,GAAG;AACnD,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,QAAQ,MAAM;AAClB,aAAS,EAAE;AACX,aAAS,EAAE;AACX,YAAQ,KAAK;AAAA,EACf;AAEA,SACE,6CAAC,SAAI,WAAU,yCACb;AAAA,gDAAC,8BAAO,WAAU,8FAA6F;AAAA,IAC/G;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,aAAY;AAAA,QACZ,cAAW;AAAA,QACX;AAAA,QACA,UAAU,CAAC,MAAM;AAAE,mBAAS,EAAE,OAAO,KAAK;AAAG,kBAAQ,IAAI;AAAA,QAAG;AAAA,QAC5D,SAAS,MAAM,QAAQ,IAAI;AAAA,QAC3B,QAAQ,MAAM,WAAW,MAAM,QAAQ,KAAK,GAAG,GAAG;AAAA,QAClD,WAAW,CAAC,MAAM;AAChB,cAAI,EAAE,QAAQ,WAAW,MAAM,KAAK,EAAE,UAAU,qCAAmB;AACjE,mBAAO,KAAK,aAAa,mBAAmB,MAAM,KAAK,CAAC,CAAC,EAAE;AAAA,UAC7D;AAAA,QACF;AAAA;AAAA,IACF;AAAA,IACC,SACC;AAAA,MAAC;AAAA;AAAA,QACC,cAAW;AAAA,QACX,WAAU;AAAA,QACV,SAAS;AAAA,QAET,sDAAC,yBAAE,WAAU,eAAc;AAAA;AAAA,IAC7B;AAAA,IAED,QAAQ,QAAQ,SAAS,KACxB,4CAAC,SAAI,WAAU,mHACZ,kBAAQ,IAAI,CAAC,QAAQ;AACpB,YAAM,QAAQ,IAAI,QAAQ;AAC1B,aACE;AAAA,QAAC,YAAAC;AAAA,QAAA;AAAA,UAEC,UAAM,2BAAe,YAAY,IAAI,eAAe;AAAA,UACpD,WAAU;AAAA,UAEV;AAAA,wDAAC,2CAAgB,KAAK,IAAI,OAAO,OAAc;AAAA,YAC/C,4CAAC,UAAK,WAAU,gCAAgC,iBAAM;AAAA;AAAA;AAAA,QALjD,IAAI;AAAA,MAMX;AAAA,IAEJ,CAAC,GACH;AAAA,KAEJ;AAEJ;AAEO,SAAS,kBAAkB;AAChC,QAAM,SAAS,kBAAkB;AACjC,QAAM,CAAC,MAAM,OAAO,QAAc,uBAAyB,QAAQ;AACnE,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,IAAI;AAC/C,QAAM,CAAC,SAAS,UAAU,QAAQ,uBAA6B,MAAS;AACxE,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,KAAK;AACpD,QAAM,CAAC,MAAM,OAAO,QAAc,uBAAS,CAAC;AAC5C,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAA0B,CAAC,CAAC;AAExE,QAAM,EAAE,aAAa,MAAM,UAAU,QAAI;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,kBAAkB,YAAY,oBAAoB,QAAQ;AAAA,IACtE;AAAA,IACA;AAAA,EACF;AAEA,QAAM,EAAE,aAAa,qBAAqB,WAAW,gBAAgB,QAAI;AAAA,IACvE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,kBAAc,qBAAO,EAAE,MAAM,WAAW,QAAQ,CAAC;AACvD,8BAAU,MAAM;AACd,UAAM,IAAI,YAAY;AACtB,QAAI,EAAE,SAAS,QAAQ,EAAE,cAAc,aAAa,EAAE,YAAY,SAAS;AACzE,kBAAY,UAAU,EAAE,MAAM,WAAW,QAAQ;AACjD,cAAQ,CAAC;AACT,wBAAkB,CAAC,CAAC;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,MAAM,WAAW,OAAO,CAAC;AAE7B,8BAAU,MAAM;AACd,QAAI,aAAa,YAAY,WAAW,EAAG;AAC3C,sBAAkB,CAAC,SAAS;AAC1B,YAAM,MAAM,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC;AACtD,YAAM,OAAO,YAAY,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,eAAe,CAAC;AAClE,aAAO,SAAS,IAAI,cAAc,CAAC,GAAG,MAAM,GAAG,IAAI;AAAA,IACrD,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,WAAW,IAAI,CAAC;AAEjC,QAAM,UAAU,MAAM,SAAS,OAAO,eAAe,SAAS,KAAK,QAAQ;AAC3E,QAAM,mBAAmB,aAAa,eAAe,WAAW;AAEhE,QAAM,gBAAgB,CAAC,CAAC,SAAS,EAAE,OAAO,OAAO,EAAE;AAEnD,QAAM,WAAW,MAAM;AACrB,iBAAa,IAAI;AAAA,EACnB;AAEA,QAAM,sBAAsB,CAAC,QAA4B;AACvD,eAAW,GAAG;AAAA,EAChB;AAEA,SACE,6CAAC,uCAAc,WAAU,8CAEvB;AAAA,iDAAC,SAAI,WAAU,kBAEb;AAAA,kDAAC,QAAG,WAAU,sBAAqB,iCAAmB;AAAA,MACtD,6CAAC,SAAI,WAAU,0CACZ;AAAA,gBAAQ,UAAU,QAAQ,OAAO,SAAS,QACzC,6CAAC,SAAI,WAAU,6FACb;AAAA,sDAAC,UAAK,WAAU,0BAA2B,kBAAO,SAAS,OAAO,OAAO,eAAe,GAAE;AAAA,UAC1F,4CAAC,UAAK,WAAU,yBAAwB,yBAAW;AAAA,WACrD;AAAA,QAED,QAAQ,SAAS,QAChB,6CAAC,SAAI,WAAU,6FACb;AAAA,sDAAC,UAAK,WAAU,0BAA0B,iBAAO,MAAM,eAAe,GAAE;AAAA,UACxE,4CAAC,UAAK,WAAU,yBAAwB,4BAAc;AAAA,WACxD;AAAA,SAEJ;AAAA,OACF;AAAA,KAEE,mBAAmB,oBAAoB,SAAS,MAChD,4CAAC,SAAI,WAAU,kDACZ,4BACG,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,4CAAC,mDAA4B,CAAG,CAAE,IAC3E,oBAAoB,IAAI,CAAC,QACvB,4CAAC,yCAAyC,YAAY,OAAjC,IAAI,eAAkC,CAC5D,GACP;AAAA,IAGF,6CAAC,SAAI,WAAU,aACb;AAAA,mDAAC,SAAI,WAAU,qCACb;AAAA,oDAAC,oBAAiB;AAAA,QAClB,6CAAC,SAAI,WAAU,sCACb;AAAA,uDAAC,wBAAO,OAAO,MAAM,eAAe,CAAC,MAAM,QAAQ,CAAmB,GACpE;AAAA,yDAAC,+BAAc,WAAU,8BAA6B,cAAW,oBAC/D;AAAA,0DAAC,mCAAY,WAAU,qCAAoC;AAAA,cAC3D,4CAAC,6BAAY;AAAA,eACf;AAAA,YACA,4CAAC,+BACE,uBAAa,IAAI,CAAC,QACjB,4CAAC,4BAA2B,OAAO,IAAI,OAAO,WAAU,WAAW,cAAI,SAAtD,IAAI,KAAwD,CAC9E,GACH;AAAA,aACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,eAAe,IAAI;AAAA,cAClC,eAAW;AAAA,gBACT;AAAA,gBACA,gBAAgB,KAAK,YAAY,SAC7B,8CACA;AAAA,cACN;AAAA,cAEA;AAAA,4DAAC,yCAAkB,WAAU,eAAc;AAAA,gBAAE;AAAA,iBAE3C,gBAAgB,KAAK,YAAY,WACjC,4CAAC,UAAK,WAAU,+GACb,2BAAiB,YAAY,SAAY,IAAI,IAChD;AAAA;AAAA;AAAA,UAEJ;AAAA,WACF;AAAA,SACF;AAAA,OACE,YAAY,UAAa,CAAC,cAC1B,6CAAC,SAAI,WAAU,qCACZ;AAAA,oBAAY,UACX,6CAAC,UAAK,WAAU,gHACb;AAAA,sBAAY,oBAAoB,4CAAC,+BAAQ,WAAU,WAAU,IAAK,4CAAC,6BAAM,WAAU,WAAU;AAAA,UAC7F,aAAa,KAAK,CAAC,MAAM,EAAE,UAAU,OAAO,GAAG;AAAA,UAChD,4CAAC,YAAO,SAAS,MAAM,WAAW,MAAS,GAAG,WAAU,gCAA+B,cAAW,wBAAuB,kBAAC;AAAA,WAC5H;AAAA,QAED,CAAC,aACA,6CAAC,UAAK,WAAU,gHAA+G;AAAA;AAAA,UAE7H,4CAAC,YAAO,SAAS,MAAM,aAAa,IAAI,GAAG,WAAU,gCAA+B,cAAW,4BAA2B,kBAAC;AAAA,WAC7H;AAAA,SAEJ;AAAA,OAEJ;AAAA,IAEA,4CAAC,wBAAO,MAAM,aAAa,cAAc,gBACvC,uDAAC,+BAAc,WAAU,qFAEvB;AAAA,mDAAC,SAAI,WAAU,+EACb;AAAA,qDAAC,6BAAY,WAAU,+CACrB;AAAA,sDAAC,yCAAkB,WAAU,wBAAuB;AAAA,UAAE;AAAA,WAExD;AAAA,SACE,gBAAgB,KAAK,YAAY,WACjC,4CAAC,wBAAO,SAAQ,SAAQ,MAAK,MAAK,WAAU,qCAAoC,SAAS,MAAM;AAAE,mBAAS;AAAG,qBAAW,MAAS;AAAA,QAAG,GAAG,uBAEvI;AAAA,SAEJ;AAAA,MAEA,6CAAC,SAAI,WAAU,8CAEb;AAAA,qDAAC,SAAI,WAAU,aACb;AAAA,sDAAC,OAAE,WAAU,gDAA+C,oBAAM;AAAA,UAClE,4CAAC,SAAI,WAAU,0BACZ,uBAAa,IAAI,CAAC,EAAE,OAAO,MAAM,MAChC;AAAA,YAAC;AAAA;AAAA,cAEC,SAAS,MAAM,oBAAoB,KAAK;AAAA,cACxC,eAAW;AAAA,gBACT;AAAA,gBACA,YAAY,QACR,0DACA;AAAA,cACN;AAAA,cAEC;AAAA,0BAAU,kBAAkB,4CAAC,6BAAM,WAAU,WAAU;AAAA,gBACvD,UAAU,qBAAqB,4CAAC,+BAAQ,WAAU,WAAU;AAAA,gBAC5D;AAAA;AAAA;AAAA,YAXI;AAAA,UAYP,CACD,GACH;AAAA,WACF;AAAA,QAEA,6CAAC,SAAI,WAAU,aACb;AAAA,sDAAC,OAAE,WAAU,gDAA+C,kBAAI;AAAA,UAChE,4CAAC,SAAI,WAAU,uBACb,uDAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;AAAA,gBACrC,eAAW;AAAA,kBACT;AAAA,kBACA,CAAC,YACG,8CACA;AAAA,gBACN;AAAA,gBAEA;AAAA,8DAAC,2BAAI,WAAU,oBAAmB;AAAA,kBAAE;AAAA;AAAA;AAAA,YAEtC;AAAA,YACA,4CAAC,6BAAS,SAAQ,iGAA2F,MAAK,QAAO;AAAA,aAC3H,GACF;AAAA,WACF;AAAA,SAEF;AAAA,MAEA,4CAAC,SAAI,WAAU,uCACb,sDAAC,wBAAO,WAAU,UAAS,SAAS,MAAM,eAAe,KAAK,GAAG,0BAEjE,GACF;AAAA,OAEF,GACF;AAAA,IAEC,mBACC,4CAAC,SAAI,WAAU,kDACZ,gBAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,4CAAC,mDAA4B,CAAG,CAAE,GAC9E,IACE,eAAe,WAAW,IAC5B,6CAAC,SAAI,WAAU,qEACb;AAAA,kDAAC,8BAAO,WAAU,sCAAqC;AAAA,MACvD,4CAAC,OAAE,WAAU,sBAAqB,kCAAoB;AAAA,MACtD,4CAAC,OAAE,WAAU,kCACV,sBACG,oCACA,6CACN;AAAA,MACC,gBAAgB,KACf,4CAAC,wBAAO,SAAQ,WAAU,MAAK,MAAK,SAAS,UAAU,2BAEvD;AAAA,OAEJ,IAEA,6CAAC,SAAI,WAAU,aACb;AAAA,kDAAC,SAAI,WAAU,kDACZ,yBAAe,IAAI,CAAC,QACnB,4CAAC,yCAAyC,YAAY,OAAjC,IAAI,eAAkC,CAC5D,GACH;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,YAAY,MAAM,QAAQ,CAAC,MAAM,IAAI,CAAC;AAAA;AAAA,MACxC;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":["useSWR","Link"]}
|
|
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, Search, X } from \"lucide-react\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.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 = 25;\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 10,\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, sort !== \"recent\"].filter(Boolean).length;\n\n const resetAll = () => {\n setHideEmpty(true);\n setSort(\"recent\");\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 && 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.single + counts.multi).toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Collections</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-5 gap-4 sm:gap-5\">\n {featuredLoading\n ? Array.from({ length: 10 }).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 <button\n onClick={() => setFiltersOpen(true)}\n aria-label=\"Filters\"\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 {(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\">Sort by</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <button\n key={value}\n onClick={() => setSort(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 sort === value\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\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\">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-5 gap-4 sm:gap-5\">\n {Array.from({ length: 10 }).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-5 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":["useSWR","Link"]}
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useRef } from "react";
|
|
4
4
|
import useSWR from "swr";
|
|
5
|
-
import { Layers, Eye, SlidersHorizontal, Award, Package,
|
|
5
|
+
import { Layers, Eye, SlidersHorizontal, Award, Package, Search, X } from "lucide-react";
|
|
6
6
|
import { Button } from "./button.js";
|
|
7
7
|
import { Dialog, DialogContent, DialogTitle } from "./dialog.js";
|
|
8
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
|
|
9
8
|
import { HelpIcon } from "./help-icon.js";
|
|
10
9
|
import { Input } from "./input.js";
|
|
11
10
|
import { ResultThumbnail } from "./result-thumbnail.js";
|
|
@@ -157,9 +156,10 @@ function CollectionsPage() {
|
|
|
157
156
|
}, [collections, isLoading, page]);
|
|
158
157
|
const hasMore = meta?.total != null ? allCollections.length < meta.total : false;
|
|
159
158
|
const isInitialLoading = isLoading && allCollections.length === 0;
|
|
160
|
-
const activeFilters = [!hideEmpty].filter(Boolean).length;
|
|
159
|
+
const activeFilters = [!hideEmpty, sort !== "recent"].filter(Boolean).length;
|
|
161
160
|
const resetAll = () => {
|
|
162
161
|
setHideEmpty(true);
|
|
162
|
+
setSort("recent");
|
|
163
163
|
};
|
|
164
164
|
const handleServiceChange = (val) => {
|
|
165
165
|
setService(val);
|
|
@@ -182,30 +182,21 @@ function CollectionsPage() {
|
|
|
182
182
|
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
183
183
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
184
184
|
/* @__PURE__ */ jsx(CollectionSearch, {}),
|
|
185
|
-
/* @__PURE__ */
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
] })
|
|
185
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 sm:ml-auto", children: /* @__PURE__ */ jsxs(
|
|
186
|
+
"button",
|
|
187
|
+
{
|
|
188
|
+
onClick: () => setFiltersOpen(true),
|
|
189
|
+
"aria-label": "Filters",
|
|
190
|
+
className: cn(
|
|
191
|
+
"relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors",
|
|
192
|
+
activeFilters > 0 || service !== void 0 ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
|
|
193
|
+
),
|
|
194
|
+
children: [
|
|
195
|
+
/* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
|
|
196
|
+
(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) })
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
) })
|
|
209
200
|
] }),
|
|
210
201
|
(service !== void 0 || !hideEmpty) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
211
202
|
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: [
|
|
@@ -231,6 +222,21 @@ function CollectionsPage() {
|
|
|
231
222
|
}, children: "Clear all" })
|
|
232
223
|
] }),
|
|
233
224
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-4 space-y-5", children: [
|
|
225
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
226
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs font-semibold text-muted-foreground", children: "Sort by" }),
|
|
227
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: SORT_OPTIONS.map(({ label, value }) => /* @__PURE__ */ jsx(
|
|
228
|
+
"button",
|
|
229
|
+
{
|
|
230
|
+
onClick: () => setSort(value),
|
|
231
|
+
className: cn(
|
|
232
|
+
"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap",
|
|
233
|
+
sort === value ? "border-primary bg-primary/10 text-primary font-medium" : "border-border text-muted-foreground hover:border-primary/50"
|
|
234
|
+
),
|
|
235
|
+
children: label
|
|
236
|
+
},
|
|
237
|
+
value
|
|
238
|
+
)) })
|
|
239
|
+
] }),
|
|
234
240
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
235
241
|
/* @__PURE__ */ jsx("p", { className: "text-2xs font-semibold text-muted-foreground", children: "Source" }),
|
|
236
242
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: SERVICE_TABS.map(({ label, value }) => /* @__PURE__ */ jsxs(
|
|
@@ -1 +1 @@
|
|
|
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 = 25;\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 10,\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 && 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.single + counts.multi).toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Collections</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-5 gap-4 sm:gap-5\">\n {featuredLoading\n ? Array.from({ length: 10 }).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-5 gap-4 sm:gap-5\">\n {Array.from({ length: 10 }).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-5 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":[]}
|
|
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, Search, X } from \"lucide-react\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.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 = 25;\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 10,\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, sort !== \"recent\"].filter(Boolean).length;\n\n const resetAll = () => {\n setHideEmpty(true);\n setSort(\"recent\");\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 && 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.single + counts.multi).toLocaleString()}</span>\n <span className=\"text-muted-foreground\">Collections</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-5 gap-4 sm:gap-5\">\n {featuredLoading\n ? Array.from({ length: 10 }).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 <button\n onClick={() => setFiltersOpen(true)}\n aria-label=\"Filters\"\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 {(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\">Sort by</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <button\n key={value}\n onClick={() => setSort(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 sort === value\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\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\">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-5 gap-4 sm:gap-5\">\n {Array.from({ length: 10 }).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-5 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":[]}
|
|
@@ -30,12 +30,13 @@ function DiscoverCollectionsStrip({
|
|
|
30
30
|
collections,
|
|
31
31
|
isLoading,
|
|
32
32
|
allCollectionsHref = "/collections",
|
|
33
|
-
title = "Collections"
|
|
33
|
+
title = "Collections",
|
|
34
|
+
icon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Layers, {})
|
|
34
35
|
}) {
|
|
35
36
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
36
37
|
import_scroll_section.ScrollSection,
|
|
37
38
|
{
|
|
38
|
-
icon
|
|
39
|
+
icon,
|
|
39
40
|
title,
|
|
40
41
|
href: allCollectionsHref,
|
|
41
42
|
linkLabel: "View all",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/discover-collections-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport { Layers } from \"lucide-react\";\nimport { ScrollSection } from \"./scroll-section.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface DiscoverCollectionsStripProps {\n collections: ApiCollection[];\n isLoading: boolean;\n\n getHref?: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n\n sectionLabel?: string;\n title?: string;\n}\n\nexport function DiscoverCollectionsStrip({\n collections,\n isLoading,\n allCollectionsHref = \"/collections\",\n title = \"Collections\",\n}: DiscoverCollectionsStripProps) {\n return (\n <ScrollSection\n icon={
|
|
1
|
+
{"version":3,"sources":["../../src/components/discover-collections-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport { Layers } from \"lucide-react\";\nimport { ScrollSection } from \"./scroll-section.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface DiscoverCollectionsStripProps {\n collections: ApiCollection[];\n isLoading: boolean;\n\n getHref?: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n\n sectionLabel?: string;\n title?: string;\n icon?: React.ReactNode;\n}\n\nexport function DiscoverCollectionsStrip({\n collections,\n isLoading,\n allCollectionsHref = \"/collections\",\n title = \"Collections\",\n icon = <Layers />,\n}: DiscoverCollectionsStripProps) {\n return (\n <ScrollSection\n icon={icon}\n title={title}\n href={allCollectionsHref}\n linkLabel=\"View all\"\n >\n {isLoading\n ? Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <CollectionCardSkeleton />\n </div>\n ))\n : collections.map((col) => (\n <div key={col.contractAddress} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <CollectionCard collection={col} />\n </div>\n ))}\n </ScrollSection>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBS;AAtBT,0BAAuB;AACvB,4BAA8B;AAC9B,6BAAuD;AAehD,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB,QAAQ;AAAA,EACR,OAAO,4CAAC,8BAAO;AACjB,GAAkC;AAChC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,WAAU;AAAA,MAET,sBACG,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MAChC,4CAAC,SAAY,WAAU,oCACrB,sDAAC,iDAAuB,KADhB,CAEV,CACD,IACD,YAAY,IAAI,CAAC,QACf,4CAAC,SAA8B,WAAU,oCACvC,sDAAC,yCAAe,YAAY,KAAK,KADzB,IAAI,eAEd,CACD;AAAA;AAAA,EACP;AAEJ;","names":[]}
|
|
@@ -8,7 +8,8 @@ interface DiscoverCollectionsStripProps {
|
|
|
8
8
|
allCollectionsHref?: string;
|
|
9
9
|
sectionLabel?: string;
|
|
10
10
|
title?: string;
|
|
11
|
+
icon?: React.ReactNode;
|
|
11
12
|
}
|
|
12
|
-
declare function DiscoverCollectionsStrip({ collections, isLoading, allCollectionsHref, title, }: DiscoverCollectionsStripProps): react_jsx_runtime.JSX.Element;
|
|
13
|
+
declare function DiscoverCollectionsStrip({ collections, isLoading, allCollectionsHref, title, icon, }: DiscoverCollectionsStripProps): react_jsx_runtime.JSX.Element;
|
|
13
14
|
|
|
14
15
|
export { DiscoverCollectionsStrip, type DiscoverCollectionsStripProps };
|
|
@@ -8,7 +8,8 @@ interface DiscoverCollectionsStripProps {
|
|
|
8
8
|
allCollectionsHref?: string;
|
|
9
9
|
sectionLabel?: string;
|
|
10
10
|
title?: string;
|
|
11
|
+
icon?: React.ReactNode;
|
|
11
12
|
}
|
|
12
|
-
declare function DiscoverCollectionsStrip({ collections, isLoading, allCollectionsHref, title, }: DiscoverCollectionsStripProps): react_jsx_runtime.JSX.Element;
|
|
13
|
+
declare function DiscoverCollectionsStrip({ collections, isLoading, allCollectionsHref, title, icon, }: DiscoverCollectionsStripProps): react_jsx_runtime.JSX.Element;
|
|
13
14
|
|
|
14
15
|
export { DiscoverCollectionsStrip, type DiscoverCollectionsStripProps };
|
|
@@ -7,12 +7,13 @@ function DiscoverCollectionsStrip({
|
|
|
7
7
|
collections,
|
|
8
8
|
isLoading,
|
|
9
9
|
allCollectionsHref = "/collections",
|
|
10
|
-
title = "Collections"
|
|
10
|
+
title = "Collections",
|
|
11
|
+
icon = /* @__PURE__ */ jsx(Layers, {})
|
|
11
12
|
}) {
|
|
12
13
|
return /* @__PURE__ */ jsx(
|
|
13
14
|
ScrollSection,
|
|
14
15
|
{
|
|
15
|
-
icon
|
|
16
|
+
icon,
|
|
16
17
|
title,
|
|
17
18
|
href: allCollectionsHref,
|
|
18
19
|
linkLabel: "View all",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/discover-collections-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport { Layers } from \"lucide-react\";\nimport { ScrollSection } from \"./scroll-section.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface DiscoverCollectionsStripProps {\n collections: ApiCollection[];\n isLoading: boolean;\n\n getHref?: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n\n sectionLabel?: string;\n title?: string;\n}\n\nexport function DiscoverCollectionsStrip({\n collections,\n isLoading,\n allCollectionsHref = \"/collections\",\n title = \"Collections\",\n}: DiscoverCollectionsStripProps) {\n return (\n <ScrollSection\n icon={
|
|
1
|
+
{"version":3,"sources":["../../src/components/discover-collections-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport { Layers } from \"lucide-react\";\nimport { ScrollSection } from \"./scroll-section.js\";\nimport { CollectionCard, CollectionCardSkeleton } from \"./collection-card.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface DiscoverCollectionsStripProps {\n collections: ApiCollection[];\n isLoading: boolean;\n\n getHref?: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n\n sectionLabel?: string;\n title?: string;\n icon?: React.ReactNode;\n}\n\nexport function DiscoverCollectionsStrip({\n collections,\n isLoading,\n allCollectionsHref = \"/collections\",\n title = \"Collections\",\n icon = <Layers />,\n}: DiscoverCollectionsStripProps) {\n return (\n <ScrollSection\n icon={icon}\n title={title}\n href={allCollectionsHref}\n linkLabel=\"View all\"\n >\n {isLoading\n ? Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <CollectionCardSkeleton />\n </div>\n ))\n : collections.map((col) => (\n <div key={col.contractAddress} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <CollectionCard collection={col} />\n </div>\n ))}\n </ScrollSection>\n );\n}\n"],"mappings":";AAwBS;AAtBT,SAAS,cAAc;AACvB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB,8BAA8B;AAehD,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB,QAAQ;AAAA,EACR,OAAO,oBAAC,UAAO;AACjB,GAAkC;AAChC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,WAAU;AAAA,MAET,sBACG,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MAChC,oBAAC,SAAY,WAAU,oCACrB,8BAAC,0BAAuB,KADhB,CAEV,CACD,IACD,YAAY,IAAI,CAAC,QACf,oBAAC,SAA8B,WAAU,oCACvC,8BAAC,kBAAe,YAAY,KAAK,KADzB,IAAI,eAEd,CACD;AAAA;AAAA,EACP;AAEJ;","names":[]}
|
|
@@ -24,6 +24,7 @@ __export(discover_page_exports, {
|
|
|
24
24
|
module.exports = __toCommonJS(discover_page_exports);
|
|
25
25
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
26
|
var import_sdk = require("@medialane/sdk");
|
|
27
|
+
var import_lucide_react = require("lucide-react");
|
|
27
28
|
var import_page_container = require("./page-container.js");
|
|
28
29
|
var import_discover_hero = require("./discover-hero.js");
|
|
29
30
|
var import_discover_collections_strip = require("./discover-collections-strip.js");
|
|
@@ -63,7 +64,8 @@ function DiscoverPage({ useMarketActions, heroBadge, heroHeadline }) {
|
|
|
63
64
|
isLoading: featuredLoading,
|
|
64
65
|
getHref: (c) => (0, import_sdk.collectionHref)("STARKNET", c.contractAddress),
|
|
65
66
|
allCollectionsHref: "/collections",
|
|
66
|
-
title: "Featured"
|
|
67
|
+
title: "Featured",
|
|
68
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Sparkles, {})
|
|
67
69
|
}
|
|
68
70
|
),
|
|
69
71
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/discover-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { assetHref, collectionHref } from \"@medialane/sdk\";\nimport { PageContainer } from \"./page-container.js\";\nimport { DiscoverHero } from \"./discover-hero.js\";\nimport { DiscoverCollectionsStrip } from \"./discover-collections-strip.js\";\nimport { DiscoverActivityStrip } from \"./discover-feed-section.js\";\nimport { DiscoverCommunitySection } from \"./discover-community-section.js\";\nimport { IpTypeNav } from \"./ip-type-nav.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollections } from \"../utils/use-collections.js\";\nimport { usePlatformStats } from \"../utils/use-platform-stats.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\n\nexport interface DiscoverPageProps {\n useMarketActions: () => CollectionMarketActions;\n heroBadge?: string;\n heroHeadline?: string;\n}\n\nexport function DiscoverPage({ useMarketActions, heroBadge, heroHeadline }: DiscoverPageProps) {\n const { walletAddress } = useMedialaneApp();\n const market = useMarketActions();\n const { stats } = usePlatformStats();\n const { orders: tickerOrders } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 14 });\n const { orders: feedOrders, isLoading: feedLoading } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 10 });\n const { collections: featured, isLoading: featuredLoading } = useCollections(1, 8, true);\n const { collections, isLoading: collectionsLoading } = useCollections(1, 8);\n\n return (\n <PageContainer className=\"box-border max-w-full space-y-14 px-4 pt-20 sm:space-y-20 sm:px-5 lg:px-6\">\n <div className=\"space-y-8 sm:space-y-10\">\n <DiscoverHero\n stats={stats ?? null}\n orders={tickerOrders}\n badgeText={heroBadge}\n headlineText={heroHeadline}\n getTickerHref={(o) => assetHref(\"STARKNET\", o.nftContract, o.nftTokenId)}\n />\n <div className=\"space-y-14 sm:space-y-20\">\n {(featuredLoading || featured.length > 0) && (\n <DiscoverCollectionsStrip\n collections={featured}\n isLoading={featuredLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n title=\"Featured\"\n />\n )}\n <DiscoverCollectionsStrip\n collections={collections}\n isLoading={collectionsLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n />\n </div>\n </div>\n <DiscoverActivityStrip\n orders={feedOrders}\n isLoading={feedLoading}\n marketplaceHref=\"/marketplace\"\n onBuyOrder={market.buy}\n isOwnOrder={(o) => !!walletAddress && !!o.offerer && isSameAddress(o.offerer, walletAddress)}\n />\n <IpTypeNav />\n <DiscoverCommunitySection />\n {market.dialogs}\n </PageContainer>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../src/components/discover-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { assetHref, collectionHref } from \"@medialane/sdk\";\nimport { Sparkles } from \"lucide-react\";\nimport { PageContainer } from \"./page-container.js\";\nimport { DiscoverHero } from \"./discover-hero.js\";\nimport { DiscoverCollectionsStrip } from \"./discover-collections-strip.js\";\nimport { DiscoverActivityStrip } from \"./discover-feed-section.js\";\nimport { DiscoverCommunitySection } from \"./discover-community-section.js\";\nimport { IpTypeNav } from \"./ip-type-nav.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollections } from \"../utils/use-collections.js\";\nimport { usePlatformStats } from \"../utils/use-platform-stats.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\n\nexport interface DiscoverPageProps {\n useMarketActions: () => CollectionMarketActions;\n heroBadge?: string;\n heroHeadline?: string;\n}\n\nexport function DiscoverPage({ useMarketActions, heroBadge, heroHeadline }: DiscoverPageProps) {\n const { walletAddress } = useMedialaneApp();\n const market = useMarketActions();\n const { stats } = usePlatformStats();\n const { orders: tickerOrders } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 14 });\n const { orders: feedOrders, isLoading: feedLoading } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 10 });\n const { collections: featured, isLoading: featuredLoading } = useCollections(1, 8, true);\n const { collections, isLoading: collectionsLoading } = useCollections(1, 8);\n\n return (\n <PageContainer className=\"box-border max-w-full space-y-14 px-4 pt-20 sm:space-y-20 sm:px-5 lg:px-6\">\n <div className=\"space-y-8 sm:space-y-10\">\n <DiscoverHero\n stats={stats ?? null}\n orders={tickerOrders}\n badgeText={heroBadge}\n headlineText={heroHeadline}\n getTickerHref={(o) => assetHref(\"STARKNET\", o.nftContract, o.nftTokenId)}\n />\n <div className=\"space-y-14 sm:space-y-20\">\n {(featuredLoading || featured.length > 0) && (\n <DiscoverCollectionsStrip\n collections={featured}\n isLoading={featuredLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n title=\"Featured\"\n icon={<Sparkles />}\n />\n )}\n <DiscoverCollectionsStrip\n collections={collections}\n isLoading={collectionsLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n />\n </div>\n </div>\n <DiscoverActivityStrip\n orders={feedOrders}\n isLoading={feedLoading}\n marketplaceHref=\"/marketplace\"\n onBuyOrder={market.buy}\n isOwnOrder={(o) => !!walletAddress && !!o.offerer && isSameAddress(o.offerer, walletAddress)}\n />\n <IpTypeNav />\n <DiscoverCommunitySection />\n {market.dialogs}\n </PageContainer>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCQ;AAjCR,iBAA0C;AAC1C,0BAAyB;AACzB,4BAA8B;AAC9B,2BAA6B;AAC7B,wCAAyC;AACzC,mCAAsC;AACtC,wCAAyC;AACzC,yBAA0B;AAC1B,2BAAgC;AAEhC,wBAA0B;AAC1B,6BAA+B;AAC/B,gCAAiC;AACjC,0BAA8B;AAQvB,SAAS,aAAa,EAAE,kBAAkB,WAAW,aAAa,GAAsB;AAC7F,QAAM,EAAE,cAAc,QAAI,sCAAgB;AAC1C,QAAM,SAAS,iBAAiB;AAChC,QAAM,EAAE,MAAM,QAAI,4CAAiB;AACnC,QAAM,EAAE,QAAQ,aAAa,QAAI,6BAAU,EAAE,QAAQ,UAAU,MAAM,UAAU,OAAO,GAAG,CAAC;AAC1F,QAAM,EAAE,QAAQ,YAAY,WAAW,YAAY,QAAI,6BAAU,EAAE,QAAQ,UAAU,MAAM,UAAU,OAAO,GAAG,CAAC;AAChH,QAAM,EAAE,aAAa,UAAU,WAAW,gBAAgB,QAAI,uCAAe,GAAG,GAAG,IAAI;AACvF,QAAM,EAAE,aAAa,WAAW,mBAAmB,QAAI,uCAAe,GAAG,CAAC;AAE1E,SACE,6CAAC,uCAAc,WAAU,6EACvB;AAAA,iDAAC,SAAI,WAAU,2BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,SAAS;AAAA,UAChB,QAAQ;AAAA,UACR,WAAW;AAAA,UACX,cAAc;AAAA,UACd,eAAe,CAAC,UAAM,sBAAU,YAAY,EAAE,aAAa,EAAE,UAAU;AAAA;AAAA,MACzE;AAAA,MACA,6CAAC,SAAI,WAAU,4BACX;AAAA,4BAAmB,SAAS,SAAS,MACrC;AAAA,UAAC;AAAA;AAAA,YACC,aAAa;AAAA,YACb,WAAW;AAAA,YACX,SAAS,CAAC,UAAM,2BAAe,YAAY,EAAE,eAAe;AAAA,YAC5D,oBAAmB;AAAA,YACnB,OAAM;AAAA,YACN,MAAM,4CAAC,gCAAS;AAAA;AAAA,QAClB;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,WAAW;AAAA,YACX,SAAS,CAAC,UAAM,2BAAe,YAAY,EAAE,eAAe;AAAA,YAC5D,oBAAmB;AAAA;AAAA,QACrB;AAAA,SACF;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,iBAAgB;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,YAAY,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC,EAAE,eAAW,mCAAc,EAAE,SAAS,aAAa;AAAA;AAAA,IAC7F;AAAA,IACA,4CAAC,gCAAU;AAAA,IACX,4CAAC,8DAAyB;AAAA,IACzB,OAAO;AAAA,KACV;AAEJ;","names":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { assetHref, collectionHref } from "@medialane/sdk";
|
|
4
|
+
import { Sparkles } from "lucide-react";
|
|
4
5
|
import { PageContainer } from "./page-container.js";
|
|
5
6
|
import { DiscoverHero } from "./discover-hero.js";
|
|
6
7
|
import { DiscoverCollectionsStrip } from "./discover-collections-strip.js";
|
|
@@ -40,7 +41,8 @@ function DiscoverPage({ useMarketActions, heroBadge, heroHeadline }) {
|
|
|
40
41
|
isLoading: featuredLoading,
|
|
41
42
|
getHref: (c) => collectionHref("STARKNET", c.contractAddress),
|
|
42
43
|
allCollectionsHref: "/collections",
|
|
43
|
-
title: "Featured"
|
|
44
|
+
title: "Featured",
|
|
45
|
+
icon: /* @__PURE__ */ jsx(Sparkles, {})
|
|
44
46
|
}
|
|
45
47
|
),
|
|
46
48
|
/* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/discover-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { assetHref, collectionHref } from \"@medialane/sdk\";\nimport { PageContainer } from \"./page-container.js\";\nimport { DiscoverHero } from \"./discover-hero.js\";\nimport { DiscoverCollectionsStrip } from \"./discover-collections-strip.js\";\nimport { DiscoverActivityStrip } from \"./discover-feed-section.js\";\nimport { DiscoverCommunitySection } from \"./discover-community-section.js\";\nimport { IpTypeNav } from \"./ip-type-nav.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollections } from \"../utils/use-collections.js\";\nimport { usePlatformStats } from \"../utils/use-platform-stats.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\n\nexport interface DiscoverPageProps {\n useMarketActions: () => CollectionMarketActions;\n heroBadge?: string;\n heroHeadline?: string;\n}\n\nexport function DiscoverPage({ useMarketActions, heroBadge, heroHeadline }: DiscoverPageProps) {\n const { walletAddress } = useMedialaneApp();\n const market = useMarketActions();\n const { stats } = usePlatformStats();\n const { orders: tickerOrders } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 14 });\n const { orders: feedOrders, isLoading: feedLoading } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 10 });\n const { collections: featured, isLoading: featuredLoading } = useCollections(1, 8, true);\n const { collections, isLoading: collectionsLoading } = useCollections(1, 8);\n\n return (\n <PageContainer className=\"box-border max-w-full space-y-14 px-4 pt-20 sm:space-y-20 sm:px-5 lg:px-6\">\n <div className=\"space-y-8 sm:space-y-10\">\n <DiscoverHero\n stats={stats ?? null}\n orders={tickerOrders}\n badgeText={heroBadge}\n headlineText={heroHeadline}\n getTickerHref={(o) => assetHref(\"STARKNET\", o.nftContract, o.nftTokenId)}\n />\n <div className=\"space-y-14 sm:space-y-20\">\n {(featuredLoading || featured.length > 0) && (\n <DiscoverCollectionsStrip\n collections={featured}\n isLoading={featuredLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n title=\"Featured\"\n />\n )}\n <DiscoverCollectionsStrip\n collections={collections}\n isLoading={collectionsLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n />\n </div>\n </div>\n <DiscoverActivityStrip\n orders={feedOrders}\n isLoading={feedLoading}\n marketplaceHref=\"/marketplace\"\n onBuyOrder={market.buy}\n isOwnOrder={(o) => !!walletAddress && !!o.offerer && isSameAddress(o.offerer, walletAddress)}\n />\n <IpTypeNav />\n <DiscoverCommunitySection />\n {market.dialogs}\n </PageContainer>\n );\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/components/discover-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { assetHref, collectionHref } from \"@medialane/sdk\";\nimport { Sparkles } from \"lucide-react\";\nimport { PageContainer } from \"./page-container.js\";\nimport { DiscoverHero } from \"./discover-hero.js\";\nimport { DiscoverCollectionsStrip } from \"./discover-collections-strip.js\";\nimport { DiscoverActivityStrip } from \"./discover-feed-section.js\";\nimport { DiscoverCommunitySection } from \"./discover-community-section.js\";\nimport { IpTypeNav } from \"./ip-type-nav.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useCollections } from \"../utils/use-collections.js\";\nimport { usePlatformStats } from \"../utils/use-platform-stats.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\n\nexport interface DiscoverPageProps {\n useMarketActions: () => CollectionMarketActions;\n heroBadge?: string;\n heroHeadline?: string;\n}\n\nexport function DiscoverPage({ useMarketActions, heroBadge, heroHeadline }: DiscoverPageProps) {\n const { walletAddress } = useMedialaneApp();\n const market = useMarketActions();\n const { stats } = usePlatformStats();\n const { orders: tickerOrders } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 14 });\n const { orders: feedOrders, isLoading: feedLoading } = useOrders({ status: \"ACTIVE\", sort: \"recent\", limit: 10 });\n const { collections: featured, isLoading: featuredLoading } = useCollections(1, 8, true);\n const { collections, isLoading: collectionsLoading } = useCollections(1, 8);\n\n return (\n <PageContainer className=\"box-border max-w-full space-y-14 px-4 pt-20 sm:space-y-20 sm:px-5 lg:px-6\">\n <div className=\"space-y-8 sm:space-y-10\">\n <DiscoverHero\n stats={stats ?? null}\n orders={tickerOrders}\n badgeText={heroBadge}\n headlineText={heroHeadline}\n getTickerHref={(o) => assetHref(\"STARKNET\", o.nftContract, o.nftTokenId)}\n />\n <div className=\"space-y-14 sm:space-y-20\">\n {(featuredLoading || featured.length > 0) && (\n <DiscoverCollectionsStrip\n collections={featured}\n isLoading={featuredLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n title=\"Featured\"\n icon={<Sparkles />}\n />\n )}\n <DiscoverCollectionsStrip\n collections={collections}\n isLoading={collectionsLoading}\n getHref={(c) => collectionHref(\"STARKNET\", c.contractAddress)}\n allCollectionsHref=\"/collections\"\n />\n </div>\n </div>\n <DiscoverActivityStrip\n orders={feedOrders}\n isLoading={feedLoading}\n marketplaceHref=\"/marketplace\"\n onBuyOrder={market.buy}\n isOwnOrder={(o) => !!walletAddress && !!o.offerer && isSameAddress(o.offerer, walletAddress)}\n />\n <IpTypeNav />\n <DiscoverCommunitySection />\n {market.dialogs}\n </PageContainer>\n );\n}\n"],"mappings":";AAmCQ,cAOA,YAPA;AAjCR,SAAS,WAAW,sBAAsB;AAC1C,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,gCAAgC;AACzC,SAAS,6BAA6B;AACtC,SAAS,gCAAgC;AACzC,SAAS,iBAAiB;AAC1B,SAAS,uBAAuB;AAEhC,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAQvB,SAAS,aAAa,EAAE,kBAAkB,WAAW,aAAa,GAAsB;AAC7F,QAAM,EAAE,cAAc,IAAI,gBAAgB;AAC1C,QAAM,SAAS,iBAAiB;AAChC,QAAM,EAAE,MAAM,IAAI,iBAAiB;AACnC,QAAM,EAAE,QAAQ,aAAa,IAAI,UAAU,EAAE,QAAQ,UAAU,MAAM,UAAU,OAAO,GAAG,CAAC;AAC1F,QAAM,EAAE,QAAQ,YAAY,WAAW,YAAY,IAAI,UAAU,EAAE,QAAQ,UAAU,MAAM,UAAU,OAAO,GAAG,CAAC;AAChH,QAAM,EAAE,aAAa,UAAU,WAAW,gBAAgB,IAAI,eAAe,GAAG,GAAG,IAAI;AACvF,QAAM,EAAE,aAAa,WAAW,mBAAmB,IAAI,eAAe,GAAG,CAAC;AAE1E,SACE,qBAAC,iBAAc,WAAU,6EACvB;AAAA,yBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,SAAS;AAAA,UAChB,QAAQ;AAAA,UACR,WAAW;AAAA,UACX,cAAc;AAAA,UACd,eAAe,CAAC,MAAM,UAAU,YAAY,EAAE,aAAa,EAAE,UAAU;AAAA;AAAA,MACzE;AAAA,MACA,qBAAC,SAAI,WAAU,4BACX;AAAA,4BAAmB,SAAS,SAAS,MACrC;AAAA,UAAC;AAAA;AAAA,YACC,aAAa;AAAA,YACb,WAAW;AAAA,YACX,SAAS,CAAC,MAAM,eAAe,YAAY,EAAE,eAAe;AAAA,YAC5D,oBAAmB;AAAA,YACnB,OAAM;AAAA,YACN,MAAM,oBAAC,YAAS;AAAA;AAAA,QAClB;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,WAAW;AAAA,YACX,SAAS,CAAC,MAAM,eAAe,YAAY,EAAE,eAAe;AAAA,YAC5D,oBAAmB;AAAA;AAAA,QACrB;AAAA,SACF;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,iBAAgB;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,YAAY,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC,EAAE,WAAW,cAAc,EAAE,SAAS,aAAa;AAAA;AAAA,IAC7F;AAAA,IACA,oBAAC,aAAU;AAAA,IACX,oBAAC,4BAAyB;AAAA,IACzB,OAAO;AAAA,KACV;AAEJ;","names":[]}
|
|
@@ -28,6 +28,7 @@ var import_react = require("react");
|
|
|
28
28
|
var import_framer_motion = require("framer-motion");
|
|
29
29
|
var import_lucide_react = require("lucide-react");
|
|
30
30
|
var import_motion_primitives = require("./motion-primitives.js");
|
|
31
|
+
var import_section_heading = require("./section-heading.js");
|
|
31
32
|
var import_format = require("../utils/format.js");
|
|
32
33
|
var import_use_image_url = require("../utils/use-image-url.js");
|
|
33
34
|
function Slide({ collection, href }) {
|
|
@@ -75,10 +76,7 @@ function Slide({ collection, href }) {
|
|
|
75
76
|
}
|
|
76
77
|
function FeaturedCarouselSkeleton() {
|
|
77
78
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_motion_primitives.FadeIn, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "space-y-4", children: [
|
|
78
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
79
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Sparkles, { className: "h-4 w-4 text-brand-purple" }),
|
|
80
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-xl font-bold", children: "Featured Collections" })
|
|
81
|
-
] }),
|
|
79
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_section_heading.SectionHeading, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Sparkles, {}), title: "Featured Collections", href: "/collections", linkLabel: "View all" }),
|
|
82
80
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse" })
|
|
83
81
|
] }) });
|
|
84
82
|
}
|
|
@@ -104,23 +102,7 @@ function FeaturedCarousel({
|
|
|
104
102
|
if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FeaturedCarouselSkeleton, {});
|
|
105
103
|
if (total === 0) return null;
|
|
106
104
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_motion_primitives.FadeIn, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "space-y-4", children: [
|
|
107
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
108
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 mt-0.5", children: [
|
|
109
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Sparkles, { className: "h-4 w-4 text-brand-purple" }),
|
|
110
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-xl font-bold", children: "Featured Collections" })
|
|
111
|
-
] }),
|
|
112
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
113
|
-
"a",
|
|
114
|
-
{
|
|
115
|
-
href: allCollectionsHref,
|
|
116
|
-
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors",
|
|
117
|
-
children: [
|
|
118
|
-
"View all ",
|
|
119
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { className: "h-3.5 w-3.5" })
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
)
|
|
123
|
-
] }),
|
|
105
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_section_heading.SectionHeading, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Sparkles, {}), title: "Featured Collections", href: allCollectionsHref, linkLabel: "View all" }),
|
|
124
106
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
125
107
|
"div",
|
|
126
108
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const resolveImage = useImageUrl();\n const name = collection.name ?? \"Featured Collection\";\n const image = resolveImage(collection.image);\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <a\n href={allCollectionsHref}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Previous\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronLeft className=\"h-4 w-4\" />\n </span>\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Next\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { SectionHeading } from \"./section-heading.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const resolveImage = useImageUrl();\n const name = collection.name ?? \"Featured Collection\";\n const image = resolveImage(collection.image);\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <SectionHeading icon={<Sparkles />} title=\"Featured Collections\" href=\"/collections\" linkLabel=\"View all\" />\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <SectionHeading icon={<Sparkles />} title=\"Featured Collections\" href={allCollectionsHref} linkLabel=\"View all\" />\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Previous\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronLeft className=\"h-4 w-4\" />\n </span>\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Next\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from "react";
|
|
|
4
4
|
import { motion, AnimatePresence } from "framer-motion";
|
|
5
5
|
import { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from "lucide-react";
|
|
6
6
|
import { FadeIn, EASE_OUT } from "./motion-primitives.js";
|
|
7
|
+
import { SectionHeading } from "./section-heading.js";
|
|
7
8
|
import { formatDisplayPrice } from "../utils/format.js";
|
|
8
9
|
import { useImageUrl } from "../utils/use-image-url.js";
|
|
9
10
|
function Slide({ collection, href }) {
|
|
@@ -51,10 +52,7 @@ function Slide({ collection, href }) {
|
|
|
51
52
|
}
|
|
52
53
|
function FeaturedCarouselSkeleton() {
|
|
53
54
|
return /* @__PURE__ */ jsx(FadeIn, { children: /* @__PURE__ */ jsxs("section", { className: "space-y-4", children: [
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-brand-purple" }),
|
|
56
|
-
/* @__PURE__ */ jsx("h2", { className: "text-xl font-bold", children: "Featured Collections" })
|
|
57
|
-
] }),
|
|
55
|
+
/* @__PURE__ */ jsx(SectionHeading, { icon: /* @__PURE__ */ jsx(Sparkles, {}), title: "Featured Collections", href: "/collections", linkLabel: "View all" }),
|
|
58
56
|
/* @__PURE__ */ jsx("div", { className: "-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse" })
|
|
59
57
|
] }) });
|
|
60
58
|
}
|
|
@@ -80,23 +78,7 @@ function FeaturedCarousel({
|
|
|
80
78
|
if (isLoading) return /* @__PURE__ */ jsx(FeaturedCarouselSkeleton, {});
|
|
81
79
|
if (total === 0) return null;
|
|
82
80
|
return /* @__PURE__ */ jsx(FadeIn, { children: /* @__PURE__ */ jsxs("section", { className: "space-y-4", children: [
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [
|
|
85
|
-
/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-brand-purple" }),
|
|
86
|
-
/* @__PURE__ */ jsx("h2", { className: "text-xl font-bold", children: "Featured Collections" })
|
|
87
|
-
] }),
|
|
88
|
-
/* @__PURE__ */ jsxs(
|
|
89
|
-
"a",
|
|
90
|
-
{
|
|
91
|
-
href: allCollectionsHref,
|
|
92
|
-
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors",
|
|
93
|
-
children: [
|
|
94
|
-
"View all ",
|
|
95
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5" })
|
|
96
|
-
]
|
|
97
|
-
}
|
|
98
|
-
)
|
|
99
|
-
] }),
|
|
81
|
+
/* @__PURE__ */ jsx(SectionHeading, { icon: /* @__PURE__ */ jsx(Sparkles, {}), title: "Featured Collections", href: allCollectionsHref, linkLabel: "View all" }),
|
|
100
82
|
/* @__PURE__ */ jsxs(
|
|
101
83
|
"div",
|
|
102
84
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const resolveImage = useImageUrl();\n const name = collection.name ?? \"Featured Collection\";\n const image = resolveImage(collection.image);\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <a\n href={allCollectionsHref}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Previous\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronLeft className=\"h-4 w-4\" />\n </span>\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Next\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { SectionHeading } from \"./section-heading.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const resolveImage = useImageUrl();\n const name = collection.name ?? \"Featured Collection\";\n const image = resolveImage(collection.image);\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <SectionHeading icon={<Sparkles />} title=\"Featured Collections\" href=\"/collections\" linkLabel=\"View all\" />\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <SectionHeading icon={<Sparkles />} title=\"Featured Collections\" href={allCollectionsHref} linkLabel=\"View all\" />\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Previous\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronLeft className=\"h-4 w-4\" />\n </span>\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Next\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -35,24 +35,12 @@ module.exports = __toCommonJS(ip_type_nav_exports);
|
|
|
35
35
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
36
|
var import_link = __toESM(require("./link.js"), 1);
|
|
37
37
|
var import_lucide_react = require("lucide-react");
|
|
38
|
+
var import_section_heading = require("./section-heading.js");
|
|
38
39
|
var import_ip_type_badge = require("./ip-type-badge.js");
|
|
39
40
|
var import_cn = require("../utils/cn.js");
|
|
40
41
|
function IpTypeNav() {
|
|
41
42
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "space-y-4", children: [
|
|
42
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
43
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-xl sm:text-2xl font-black", children: "Browse by Type" }),
|
|
44
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
45
|
-
import_link.default,
|
|
46
|
-
{
|
|
47
|
-
href: "/nft",
|
|
48
|
-
className: "flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
49
|
-
children: [
|
|
50
|
-
"View all ",
|
|
51
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { className: "h-3.5 w-3.5" })
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
)
|
|
55
|
-
] }),
|
|
43
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_section_heading.SectionHeading, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.LayoutGrid, {}), title: "Browse by Type", href: "/nft", linkLabel: "View all" }),
|
|
56
44
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-3", children: import_ip_type_badge.IP_TYPE_CONFIG.map(({ slug, label, icon: Icon, colorClass, bgClass }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
57
45
|
import_link.default,
|
|
58
46
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ip-type-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/components/ip-type-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { LayoutGrid } from \"lucide-react\";\nimport { SectionHeading } from \"./section-heading.js\";\nimport { IP_TYPE_CONFIG } from \"./ip-type-badge.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport function IpTypeNav() {\n return (\n <section className=\"space-y-4\">\n <SectionHeading icon={<LayoutGrid />} title=\"Browse by Type\" href=\"/nft\" linkLabel=\"View all\" />\n\n <div className=\"grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-3\">\n {IP_TYPE_CONFIG.map(({ slug, label, icon: Icon, colorClass, bgClass }) => (\n <Link\n key={slug}\n href={`/${slug}`}\n className=\"group flex flex-col items-center gap-2.5 p-4 rounded-xl border border-border bg-card hover:border-primary/40 hover:bg-card/80 transition-all hover:shadow-md hover:shadow-black/10\"\n >\n <div className={cn(\"h-10 w-10 rounded-xl flex items-center justify-center\", bgClass)}>\n <Icon className={cn(\"h-5 w-5\", colorClass)} />\n </div>\n <span className=\"text-xs font-semibold text-center leading-tight\">{label}</span>\n </Link>\n ))}\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAW4B;AAT5B,kBAAiB;AACjB,0BAA2B;AAC3B,6BAA+B;AAC/B,2BAA+B;AAC/B,gBAAmB;AAEZ,SAAS,YAAY;AAC1B,SACE,6CAAC,aAAQ,WAAU,aACjB;AAAA,gDAAC,yCAAe,MAAM,4CAAC,kCAAW,GAAI,OAAM,kBAAiB,MAAK,QAAO,WAAU,YAAW;AAAA,IAE9F,4CAAC,SAAI,WAAU,wDACZ,8CAAe,IAAI,CAAC,EAAE,MAAM,OAAO,MAAM,MAAM,YAAY,QAAQ,MAClE;AAAA,MAAC,YAAAA;AAAA,MAAA;AAAA,QAEC,MAAM,IAAI,IAAI;AAAA,QACd,WAAU;AAAA,QAEV;AAAA,sDAAC,SAAI,eAAW,cAAG,yDAAyD,OAAO,GACjF,sDAAC,QAAK,eAAW,cAAG,WAAW,UAAU,GAAG,GAC9C;AAAA,UACA,4CAAC,UAAK,WAAU,mDAAmD,iBAAM;AAAA;AAAA;AAAA,MAPpE;AAAA,IAQP,CACD,GACH;AAAA,KACF;AAEJ;","names":["Link"]}
|
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import Link from "./link.js";
|
|
4
|
-
import {
|
|
4
|
+
import { LayoutGrid } from "lucide-react";
|
|
5
|
+
import { SectionHeading } from "./section-heading.js";
|
|
5
6
|
import { IP_TYPE_CONFIG } from "./ip-type-badge.js";
|
|
6
7
|
import { cn } from "../utils/cn.js";
|
|
7
8
|
function IpTypeNav() {
|
|
8
9
|
return /* @__PURE__ */ jsxs("section", { className: "space-y-4", children: [
|
|
9
|
-
/* @__PURE__ */
|
|
10
|
-
/* @__PURE__ */ jsx("h2", { className: "text-xl sm:text-2xl font-black", children: "Browse by Type" }),
|
|
11
|
-
/* @__PURE__ */ jsxs(
|
|
12
|
-
Link,
|
|
13
|
-
{
|
|
14
|
-
href: "/nft",
|
|
15
|
-
className: "flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
16
|
-
children: [
|
|
17
|
-
"View all ",
|
|
18
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5" })
|
|
19
|
-
]
|
|
20
|
-
}
|
|
21
|
-
)
|
|
22
|
-
] }),
|
|
10
|
+
/* @__PURE__ */ jsx(SectionHeading, { icon: /* @__PURE__ */ jsx(LayoutGrid, {}), title: "Browse by Type", href: "/nft", linkLabel: "View all" }),
|
|
23
11
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-3", children: IP_TYPE_CONFIG.map(({ slug, label, icon: Icon, colorClass, bgClass }) => /* @__PURE__ */ jsxs(
|
|
24
12
|
Link,
|
|
25
13
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ip-type-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/components/ip-type-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { LayoutGrid } from \"lucide-react\";\nimport { SectionHeading } from \"./section-heading.js\";\nimport { IP_TYPE_CONFIG } from \"./ip-type-badge.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport function IpTypeNav() {\n return (\n <section className=\"space-y-4\">\n <SectionHeading icon={<LayoutGrid />} title=\"Browse by Type\" href=\"/nft\" linkLabel=\"View all\" />\n\n <div className=\"grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-3\">\n {IP_TYPE_CONFIG.map(({ slug, label, icon: Icon, colorClass, bgClass }) => (\n <Link\n key={slug}\n href={`/${slug}`}\n className=\"group flex flex-col items-center gap-2.5 p-4 rounded-xl border border-border bg-card hover:border-primary/40 hover:bg-card/80 transition-all hover:shadow-md hover:shadow-black/10\"\n >\n <div className={cn(\"h-10 w-10 rounded-xl flex items-center justify-center\", bgClass)}>\n <Icon className={cn(\"h-5 w-5\", colorClass)} />\n </div>\n <span className=\"text-xs font-semibold text-center leading-tight\">{label}</span>\n </Link>\n ))}\n </div>\n </section>\n );\n}\n"],"mappings":";AAW4B,cAIlB,YAJkB;AAT5B,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,UAAU;AAEZ,SAAS,YAAY;AAC1B,SACE,qBAAC,aAAQ,WAAU,aACjB;AAAA,wBAAC,kBAAe,MAAM,oBAAC,cAAW,GAAI,OAAM,kBAAiB,MAAK,QAAO,WAAU,YAAW;AAAA,IAE9F,oBAAC,SAAI,WAAU,wDACZ,yBAAe,IAAI,CAAC,EAAE,MAAM,OAAO,MAAM,MAAM,YAAY,QAAQ,MAClE;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM,IAAI,IAAI;AAAA,QACd,WAAU;AAAA,QAEV;AAAA,8BAAC,SAAI,WAAW,GAAG,yDAAyD,OAAO,GACjF,8BAAC,QAAK,WAAW,GAAG,WAAW,UAAU,GAAG,GAC9C;AAAA,UACA,oBAAC,UAAK,WAAU,mDAAmD,iBAAM;AAAA;AAAA;AAAA,MAPpE;AAAA,IAQP,CACD,GACH;AAAA,KACF;AAEJ;","names":[]}
|