@medialane/ui 0.114.0 → 0.115.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/token-card.cjs +17 -6
- package/dist/components/token-card.cjs.map +1 -1
- package/dist/components/token-card.d.cts +8 -1
- package/dist/components/token-card.d.ts +8 -1
- package/dist/components/token-card.js +18 -7
- package/dist/components/token-card.js.map +1 -1
- package/package.json +1 -1
|
@@ -55,7 +55,8 @@ function TokenCard({
|
|
|
55
55
|
onCancel,
|
|
56
56
|
onBuy,
|
|
57
57
|
onOffer,
|
|
58
|
-
onReport
|
|
58
|
+
onReport,
|
|
59
|
+
usdValue
|
|
59
60
|
}) {
|
|
60
61
|
const name = token.metadata?.name || `Token #${token.tokenId}`;
|
|
61
62
|
const image = (0, import_ipfs.ipfsToHttp)(token.metadata?.image);
|
|
@@ -101,11 +102,21 @@ function TokenCard({
|
|
|
101
102
|
] }) })
|
|
102
103
|
}
|
|
103
104
|
),
|
|
104
|
-
listingOrder &&
|
|
105
|
-
|
|
106
|
-
(0, import_format.
|
|
107
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "
|
|
108
|
-
|
|
105
|
+
listingOrder && (() => {
|
|
106
|
+
const cryptoDisplay = (0, import_format.formatDisplayPrice)(listingOrder.price.formatted);
|
|
107
|
+
const stable = (0, import_format.isStableCurrency)(listingOrder.price.currency);
|
|
108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute bottom-2 right-2 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums", children: usdValue ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
109
|
+
usdValue,
|
|
110
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-muted-foreground/50", children: "\xB7" }),
|
|
111
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "inline-flex items-center gap-1 font-semibold text-muted-foreground", children: [
|
|
112
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_currency_icon.CurrencyIcon, { symbol: listingOrder.price.currency ?? "", size: 12 }),
|
|
113
|
+
stable ? listingOrder.price.currency : cryptoDisplay
|
|
114
|
+
] })
|
|
115
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
116
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_currency_icon.CurrencyIcon, { symbol: listingOrder.price.currency ?? "", size: 13 }),
|
|
117
|
+
cryptoDisplay
|
|
118
|
+
] }) }) });
|
|
119
|
+
})(),
|
|
109
120
|
(token.metadataStatus === "PENDING" || token.metadataStatus === "FETCHING") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5", children: [
|
|
110
121
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-3 w-3 animate-spin text-white/70" }),
|
|
111
122
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-2xs text-white/70", children: "Indexing\u2026" })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n}: TokenCardProps) {\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = ipfsToHttp(token.metadata?.image);\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n // First listing-type active order (offer.itemType = ERC721 or ERC1155)\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const creatorAttr = token.metadata?.attributes?.find((a) => a.trait_type === \"Creator\");\n const creator = typeof creatorAttr?.value === \"string\" ? creatorAttr.value : null;\n const owner = token.balances?.[0]?.owner ?? token.owner ?? null;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base relative overflow-hidden flex flex-col w-full\">\n\n {/* ── Clickable image + info ─────────────────────────────── */}\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n {/* Image */}\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl tabular-nums text-muted-foreground\">#{token.tokenId}</span>\n </div>\n }\n />\n\n {/* Price chip — bottom right overlay */}\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1 px-2 py-1 rounded-full text-2xs font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm\">\n <CurrencyIcon symbol={listingOrder.price.currency ?? \"\"} size={10} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n <span className=\"text-muted-foreground font-normal\">{listingOrder.price.currency}</span>\n </span>\n </div>\n )}\n\n {/* Indexing indicator */}\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n <div className=\"flex items-center gap-1.5 mt-1.5 flex-wrap\">\n {ipType && <IpTypeBadge ipType={ipType as any} size=\"sm\" />}\n {creator ? (\n <p className=\"text-2xs tabular-nums text-muted-foreground/60 truncate\">\n {creator.slice(0, 8)}…{creator.slice(-6)}\n </p>\n ) : token.metadata?.description ? (\n <p className=\"text-2xs text-muted-foreground truncate leading-snug\">\n {token.metadata.description}\n </p>\n ) : null}\n </div>\n </div>\n </Link>\n\n {/* ── Action row ────────────────────────────────────────── */}\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n {/* Expand */}\n <Link\n href={assetHref}\n className={cn(BTN_OUTLINE, \"w-8 shrink-0 px-0\")}\n title=\"View asset\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n </Link>\n\n {/* Owner: List (or Listed label) — Buyer: Buy / Make offer */}\n {isOwner ? (\n listingOrder ? (\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5 mr-1.5\" />\n View\n </Link>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n )\n ) : listingOrder ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n )}\n\n {/* 3-dot menu */}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {(owner ?? creator) && (\n <DropdownMenuItem asChild>\n <Link href={`/account/${owner ?? creator}`} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View owner\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n <div className=\"h-2.5 w-2/5 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["buildAssetHref","buildCollectionHref","Link"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n /**\n * Pre-formatted USD equivalent of the active listing price (e.g.\n * \"$13.15\"), computed by the host from its own live rate feed — this\n * package has no price-feed access by design. Omit/null renders the\n * crypto-only chip.\n */\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = ipfsToHttp(token.metadata?.image);\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n // First listing-type active order (offer.itemType = ERC721 or ERC1155)\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const creatorAttr = token.metadata?.attributes?.find((a) => a.trait_type === \"Creator\");\n const creator = typeof creatorAttr?.value === \"string\" ? creatorAttr.value : null;\n const owner = token.balances?.[0]?.owner ?? token.owner ?? null;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base relative overflow-hidden flex flex-col w-full\">\n\n {/* ── Clickable image + info ─────────────────────────────── */}\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n {/* Image */}\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl tabular-nums text-muted-foreground\">#{token.tokenId}</span>\n </div>\n }\n />\n\n {/* Price chip — bottom right overlay. Fiat leads when a live rate\n is available; the crypto amount trails, dimmer, middot-\n separated — same language as AssetCard's pill. Stablecoins\n collapse to fiat + symbol alone (no duplicate number). */}\n {listingOrder && (() => {\n const cryptoDisplay = formatDisplayPrice(listingOrder.price.formatted);\n const stable = isStableCurrency(listingOrder.price.currency);\n return (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n {usdValue ? (\n <>\n {usdValue}\n <span className=\"text-muted-foreground/50\">·</span>\n <span className=\"inline-flex items-center gap-1 font-semibold text-muted-foreground\">\n <CurrencyIcon symbol={listingOrder.price.currency ?? \"\"} size={12} />\n {stable ? listingOrder.price.currency : cryptoDisplay}\n </span>\n </>\n ) : (\n <>\n <CurrencyIcon symbol={listingOrder.price.currency ?? \"\"} size={13} />\n {cryptoDisplay}\n </>\n )}\n </span>\n </div>\n );\n })()}\n\n {/* Indexing indicator */}\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n <div className=\"flex items-center gap-1.5 mt-1.5 flex-wrap\">\n {ipType && <IpTypeBadge ipType={ipType as any} size=\"sm\" />}\n {creator ? (\n <p className=\"text-2xs tabular-nums text-muted-foreground/60 truncate\">\n {creator.slice(0, 8)}…{creator.slice(-6)}\n </p>\n ) : token.metadata?.description ? (\n <p className=\"text-2xs text-muted-foreground truncate leading-snug\">\n {token.metadata.description}\n </p>\n ) : null}\n </div>\n </div>\n </Link>\n\n {/* ── Action row ────────────────────────────────────────── */}\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n {/* Expand */}\n <Link\n href={assetHref}\n className={cn(BTN_OUTLINE, \"w-8 shrink-0 px-0\")}\n title=\"View asset\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n </Link>\n\n {/* Owner: List (or Listed label) — Buyer: Buy / Make offer */}\n {isOwner ? (\n listingOrder ? (\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5 mr-1.5\" />\n View\n </Link>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n )\n ) : listingOrder ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n )}\n\n {/* 3-dot menu */}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {(owner ?? creator) && (\n <DropdownMenuItem asChild>\n <Link href={`/account/${owner ?? creator}`} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View owner\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n <div className=\"h-2.5 w-2/5 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["buildAssetHref","buildCollectionHref","Link"]}
|
|
@@ -10,8 +10,15 @@ interface TokenCardProps {
|
|
|
10
10
|
onBuy?: (token: ApiToken) => void;
|
|
11
11
|
onOffer?: (token: ApiToken) => void;
|
|
12
12
|
onReport?: (token: ApiToken) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Pre-formatted USD equivalent of the active listing price (e.g.
|
|
15
|
+
* "$13.15"), computed by the host from its own live rate feed — this
|
|
16
|
+
* package has no price-feed access by design. Omit/null renders the
|
|
17
|
+
* crypto-only chip.
|
|
18
|
+
*/
|
|
19
|
+
usdValue?: string | null;
|
|
13
20
|
}
|
|
14
|
-
declare function TokenCard({ token, isOwner, onList, onTransfer, onCancel, onBuy, onOffer, onReport, }: TokenCardProps): react_jsx_runtime.JSX.Element;
|
|
21
|
+
declare function TokenCard({ token, isOwner, onList, onTransfer, onCancel, onBuy, onOffer, onReport, usdValue, }: TokenCardProps): react_jsx_runtime.JSX.Element;
|
|
15
22
|
declare function TokenCardSkeleton(): react_jsx_runtime.JSX.Element;
|
|
16
23
|
|
|
17
24
|
export { TokenCard, type TokenCardProps, TokenCardSkeleton };
|
|
@@ -10,8 +10,15 @@ interface TokenCardProps {
|
|
|
10
10
|
onBuy?: (token: ApiToken) => void;
|
|
11
11
|
onOffer?: (token: ApiToken) => void;
|
|
12
12
|
onReport?: (token: ApiToken) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Pre-formatted USD equivalent of the active listing price (e.g.
|
|
15
|
+
* "$13.15"), computed by the host from its own live rate feed — this
|
|
16
|
+
* package has no price-feed access by design. Omit/null renders the
|
|
17
|
+
* crypto-only chip.
|
|
18
|
+
*/
|
|
19
|
+
usdValue?: string | null;
|
|
13
20
|
}
|
|
14
|
-
declare function TokenCard({ token, isOwner, onList, onTransfer, onCancel, onBuy, onOffer, onReport, }: TokenCardProps): react_jsx_runtime.JSX.Element;
|
|
21
|
+
declare function TokenCard({ token, isOwner, onList, onTransfer, onCancel, onBuy, onOffer, onReport, usdValue, }: TokenCardProps): react_jsx_runtime.JSX.Element;
|
|
15
22
|
declare function TokenCardSkeleton(): react_jsx_runtime.JSX.Element;
|
|
16
23
|
|
|
17
24
|
export { TokenCard, type TokenCardProps, TokenCardSkeleton };
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "./dropdown-menu.js";
|
|
25
25
|
import { cn } from "../utils/cn.js";
|
|
26
26
|
import { ipfsToHttp } from "../utils/ipfs.js";
|
|
27
|
-
import { formatDisplayPrice } from "../utils/format.js";
|
|
27
|
+
import { formatDisplayPrice, isStableCurrency } from "../utils/format.js";
|
|
28
28
|
import { CurrencyIcon } from "./currency-icon.js";
|
|
29
29
|
import { IpTypeBadge } from "./ip-type-badge.js";
|
|
30
30
|
import { AnimatedTokenMedia } from "./animated-token-media.js";
|
|
@@ -40,7 +40,8 @@ function TokenCard({
|
|
|
40
40
|
onCancel,
|
|
41
41
|
onBuy,
|
|
42
42
|
onOffer,
|
|
43
|
-
onReport
|
|
43
|
+
onReport,
|
|
44
|
+
usdValue
|
|
44
45
|
}) {
|
|
45
46
|
const name = token.metadata?.name || `Token #${token.tokenId}`;
|
|
46
47
|
const image = ipfsToHttp(token.metadata?.image);
|
|
@@ -86,11 +87,21 @@ function TokenCard({
|
|
|
86
87
|
] }) })
|
|
87
88
|
}
|
|
88
89
|
),
|
|
89
|
-
listingOrder &&
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
/* @__PURE__ */ jsx("
|
|
93
|
-
|
|
90
|
+
listingOrder && (() => {
|
|
91
|
+
const cryptoDisplay = formatDisplayPrice(listingOrder.price.formatted);
|
|
92
|
+
const stable = isStableCurrency(listingOrder.price.currency);
|
|
93
|
+
return /* @__PURE__ */ jsx("div", { className: "absolute bottom-2 right-2 z-10", children: /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums", children: usdValue ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
94
|
+
usdValue,
|
|
95
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/50", children: "\xB7" }),
|
|
96
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 font-semibold text-muted-foreground", children: [
|
|
97
|
+
/* @__PURE__ */ jsx(CurrencyIcon, { symbol: listingOrder.price.currency ?? "", size: 12 }),
|
|
98
|
+
stable ? listingOrder.price.currency : cryptoDisplay
|
|
99
|
+
] })
|
|
100
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
101
|
+
/* @__PURE__ */ jsx(CurrencyIcon, { symbol: listingOrder.price.currency ?? "", size: 13 }),
|
|
102
|
+
cryptoDisplay
|
|
103
|
+
] }) }) });
|
|
104
|
+
})(),
|
|
94
105
|
(token.metadataStatus === "PENDING" || token.metadataStatus === "FETCHING") && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5", children: [
|
|
95
106
|
/* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin text-white/70" }),
|
|
96
107
|
/* @__PURE__ */ jsx("span", { className: "text-2xs text-white/70", children: "Indexing\u2026" })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n}: TokenCardProps) {\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = ipfsToHttp(token.metadata?.image);\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n // First listing-type active order (offer.itemType = ERC721 or ERC1155)\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const creatorAttr = token.metadata?.attributes?.find((a) => a.trait_type === \"Creator\");\n const creator = typeof creatorAttr?.value === \"string\" ? creatorAttr.value : null;\n const owner = token.balances?.[0]?.owner ?? token.owner ?? null;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base relative overflow-hidden flex flex-col w-full\">\n\n {/* ── Clickable image + info ─────────────────────────────── */}\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n {/* Image */}\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl tabular-nums text-muted-foreground\">#{token.tokenId}</span>\n </div>\n }\n />\n\n {/* Price chip — bottom right overlay */}\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1 px-2 py-1 rounded-full text-2xs font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm\">\n <CurrencyIcon symbol={listingOrder.price.currency ?? \"\"} size={10} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n <span className=\"text-muted-foreground font-normal\">{listingOrder.price.currency}</span>\n </span>\n </div>\n )}\n\n {/* Indexing indicator */}\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n <div className=\"flex items-center gap-1.5 mt-1.5 flex-wrap\">\n {ipType && <IpTypeBadge ipType={ipType as any} size=\"sm\" />}\n {creator ? (\n <p className=\"text-2xs tabular-nums text-muted-foreground/60 truncate\">\n {creator.slice(0, 8)}…{creator.slice(-6)}\n </p>\n ) : token.metadata?.description ? (\n <p className=\"text-2xs text-muted-foreground truncate leading-snug\">\n {token.metadata.description}\n </p>\n ) : null}\n </div>\n </div>\n </Link>\n\n {/* ── Action row ────────────────────────────────────────── */}\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n {/* Expand */}\n <Link\n href={assetHref}\n className={cn(BTN_OUTLINE, \"w-8 shrink-0 px-0\")}\n title=\"View asset\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n </Link>\n\n {/* Owner: List (or Listed label) — Buyer: Buy / Make offer */}\n {isOwner ? (\n listingOrder ? (\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5 mr-1.5\" />\n View\n </Link>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n )\n ) : listingOrder ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n )}\n\n {/* 3-dot menu */}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {(owner ?? creator) && (\n <DropdownMenuItem asChild>\n <Link href={`/account/${owner ?? creator}`} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View owner\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n <div className=\"h-2.5 w-2/5 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n /**\n * Pre-formatted USD equivalent of the active listing price (e.g.\n * \"$13.15\"), computed by the host from its own live rate feed — this\n * package has no price-feed access by design. Omit/null renders the\n * crypto-only chip.\n */\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = ipfsToHttp(token.metadata?.image);\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n // First listing-type active order (offer.itemType = ERC721 or ERC1155)\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const creatorAttr = token.metadata?.attributes?.find((a) => a.trait_type === \"Creator\");\n const creator = typeof creatorAttr?.value === \"string\" ? creatorAttr.value : null;\n const owner = token.balances?.[0]?.owner ?? token.owner ?? null;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base relative overflow-hidden flex flex-col w-full\">\n\n {/* ── Clickable image + info ─────────────────────────────── */}\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n {/* Image */}\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl tabular-nums text-muted-foreground\">#{token.tokenId}</span>\n </div>\n }\n />\n\n {/* Price chip — bottom right overlay. Fiat leads when a live rate\n is available; the crypto amount trails, dimmer, middot-\n separated — same language as AssetCard's pill. Stablecoins\n collapse to fiat + symbol alone (no duplicate number). */}\n {listingOrder && (() => {\n const cryptoDisplay = formatDisplayPrice(listingOrder.price.formatted);\n const stable = isStableCurrency(listingOrder.price.currency);\n return (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n {usdValue ? (\n <>\n {usdValue}\n <span className=\"text-muted-foreground/50\">·</span>\n <span className=\"inline-flex items-center gap-1 font-semibold text-muted-foreground\">\n <CurrencyIcon symbol={listingOrder.price.currency ?? \"\"} size={12} />\n {stable ? listingOrder.price.currency : cryptoDisplay}\n </span>\n </>\n ) : (\n <>\n <CurrencyIcon symbol={listingOrder.price.currency ?? \"\"} size={13} />\n {cryptoDisplay}\n </>\n )}\n </span>\n </div>\n );\n })()}\n\n {/* Indexing indicator */}\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n <div className=\"flex items-center gap-1.5 mt-1.5 flex-wrap\">\n {ipType && <IpTypeBadge ipType={ipType as any} size=\"sm\" />}\n {creator ? (\n <p className=\"text-2xs tabular-nums text-muted-foreground/60 truncate\">\n {creator.slice(0, 8)}…{creator.slice(-6)}\n </p>\n ) : token.metadata?.description ? (\n <p className=\"text-2xs text-muted-foreground truncate leading-snug\">\n {token.metadata.description}\n </p>\n ) : null}\n </div>\n </div>\n </Link>\n\n {/* ── Action row ────────────────────────────────────────── */}\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n {/* Expand */}\n <Link\n href={assetHref}\n className={cn(BTN_OUTLINE, \"w-8 shrink-0 px-0\")}\n title=\"View asset\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n </Link>\n\n {/* Owner: List (or Listed label) — Buyer: Buy / Make offer */}\n {isOwner ? (\n listingOrder ? (\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5 mr-1.5\" />\n View\n </Link>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n )\n ) : listingOrder ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n )}\n\n {/* 3-dot menu */}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {(owner ?? creator) && (\n <DropdownMenuItem asChild>\n <Link href={`/account/${owner ?? creator}`} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View owner\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n <div className=\"h-2.5 w-2/5 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|