@medialane/ui 0.99.0 → 0.100.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/README.md +19 -0
- package/dist/components/action-button.cjs +1 -3
- package/dist/components/action-button.cjs.map +1 -1
- package/dist/components/action-button.js +1 -3
- package/dist/components/action-button.js.map +1 -1
- package/dist/components/activity-card.cjs +3 -3
- package/dist/components/activity-card.cjs.map +1 -1
- package/dist/components/activity-card.js +3 -3
- package/dist/components/activity-card.js.map +1 -1
- package/dist/components/activity-feed-shell.cjs +1 -1
- package/dist/components/activity-feed-shell.cjs.map +1 -1
- package/dist/components/activity-feed-shell.js +1 -1
- package/dist/components/activity-feed-shell.js.map +1 -1
- package/dist/components/activity-row.cjs +3 -3
- package/dist/components/activity-row.cjs.map +1 -1
- package/dist/components/activity-row.js +3 -3
- package/dist/components/activity-row.js.map +1 -1
- package/dist/components/activity-ticker.cjs +1 -1
- package/dist/components/activity-ticker.cjs.map +1 -1
- package/dist/components/activity-ticker.js +1 -1
- package/dist/components/activity-ticker.js.map +1 -1
- package/dist/components/asset-card.cjs +2 -2
- package/dist/components/asset-card.cjs.map +1 -1
- package/dist/components/asset-card.js +2 -2
- package/dist/components/asset-card.js.map +1 -1
- package/dist/components/asset-license-summary.cjs +2 -2
- package/dist/components/asset-license-summary.cjs.map +1 -1
- package/dist/components/asset-license-summary.js +2 -2
- package/dist/components/asset-license-summary.js.map +1 -1
- package/dist/components/asset-overview-content.cjs +2 -2
- package/dist/components/asset-overview-content.cjs.map +1 -1
- package/dist/components/asset-overview-content.js +2 -2
- package/dist/components/asset-overview-content.js.map +1 -1
- package/dist/components/asset-picker-cell.cjs +1 -1
- package/dist/components/asset-picker-cell.cjs.map +1 -1
- package/dist/components/asset-picker-cell.js +1 -1
- package/dist/components/asset-picker-cell.js.map +1 -1
- package/dist/components/asset-top-sections.cjs +1 -1
- package/dist/components/asset-top-sections.cjs.map +1 -1
- package/dist/components/asset-top-sections.js +1 -1
- package/dist/components/asset-top-sections.js.map +1 -1
- package/dist/components/asset-utility-icons.cjs +2 -2
- package/dist/components/asset-utility-icons.cjs.map +1 -1
- package/dist/components/asset-utility-icons.js +2 -2
- package/dist/components/asset-utility-icons.js.map +1 -1
- package/dist/components/claim-rail.cjs +1 -1
- package/dist/components/claim-rail.cjs.map +1 -1
- package/dist/components/claim-rail.js +1 -1
- package/dist/components/claim-rail.js.map +1 -1
- package/dist/components/coin-card.cjs +4 -4
- package/dist/components/coin-card.cjs.map +1 -1
- package/dist/components/coin-card.js +4 -4
- package/dist/components/coin-card.js.map +1 -1
- package/dist/components/coin-launch-preview.cjs +5 -5
- package/dist/components/coin-launch-preview.cjs.map +1 -1
- package/dist/components/coin-launch-preview.js +5 -5
- package/dist/components/coin-launch-preview.js.map +1 -1
- package/dist/components/coins-explorer.cjs +3 -3
- package/dist/components/coins-explorer.cjs.map +1 -1
- package/dist/components/coins-explorer.js +3 -3
- package/dist/components/coins-explorer.js.map +1 -1
- package/dist/components/collection-card.cjs +6 -6
- package/dist/components/collection-card.cjs.map +1 -1
- package/dist/components/collection-card.js +6 -6
- package/dist/components/collection-card.js.map +1 -1
- package/dist/components/collection-filters.cjs +5 -5
- package/dist/components/collection-filters.cjs.map +1 -1
- package/dist/components/collection-filters.js +5 -5
- package/dist/components/collection-filters.js.map +1 -1
- package/dist/components/community-rewards-section.cjs +2 -2
- package/dist/components/community-rewards-section.cjs.map +1 -1
- package/dist/components/community-rewards-section.js +2 -2
- package/dist/components/community-rewards-section.js.map +1 -1
- package/dist/components/discover-feed-section.cjs +1 -1
- package/dist/components/discover-feed-section.cjs.map +1 -1
- package/dist/components/discover-feed-section.js +1 -1
- package/dist/components/discover-feed-section.js.map +1 -1
- package/dist/components/featured-carousel.cjs +4 -4
- package/dist/components/featured-carousel.cjs.map +1 -1
- package/dist/components/featured-carousel.js +4 -4
- package/dist/components/featured-carousel.js.map +1 -1
- package/dist/components/gradient-button.cjs +1 -1
- package/dist/components/gradient-button.cjs.map +1 -1
- package/dist/components/gradient-button.js +1 -1
- package/dist/components/gradient-button.js.map +1 -1
- package/dist/components/hero-slider.cjs +2 -2
- package/dist/components/hero-slider.cjs.map +1 -1
- package/dist/components/hero-slider.js +2 -2
- package/dist/components/hero-slider.js.map +1 -1
- package/dist/components/launchpad-filter-bar.cjs +1 -1
- package/dist/components/launchpad-filter-bar.cjs.map +1 -1
- package/dist/components/launchpad-filter-bar.js +1 -1
- package/dist/components/launchpad-filter-bar.js.map +1 -1
- package/dist/components/launchpad-services.cjs +5 -5
- package/dist/components/launchpad-services.cjs.map +1 -1
- package/dist/components/launchpad-services.js +5 -5
- package/dist/components/launchpad-services.js.map +1 -1
- package/dist/components/launchpad-strip.cjs +4 -4
- package/dist/components/launchpad-strip.cjs.map +1 -1
- package/dist/components/launchpad-strip.js +4 -4
- package/dist/components/launchpad-strip.js.map +1 -1
- package/dist/components/listing-card.cjs +7 -7
- package/dist/components/listing-card.cjs.map +1 -1
- package/dist/components/listing-card.js +7 -7
- package/dist/components/listing-card.js.map +1 -1
- package/dist/components/medialane-collection-card.cjs +2 -2
- package/dist/components/medialane-collection-card.cjs.map +1 -1
- package/dist/components/medialane-collection-card.js +2 -2
- package/dist/components/medialane-collection-card.js.map +1 -1
- package/dist/components/nav-command-menu.cjs +5 -5
- package/dist/components/nav-command-menu.cjs.map +1 -1
- package/dist/components/nav-command-menu.js +5 -5
- package/dist/components/nav-command-menu.js.map +1 -1
- package/dist/components/nav-shell.cjs +2 -2
- package/dist/components/nav-shell.cjs.map +1 -1
- package/dist/components/nav-shell.js +2 -2
- package/dist/components/nav-shell.js.map +1 -1
- package/dist/components/portfolio-chip-filter.cjs +1 -1
- package/dist/components/portfolio-chip-filter.cjs.map +1 -1
- package/dist/components/portfolio-chip-filter.js +1 -1
- package/dist/components/portfolio-chip-filter.js.map +1 -1
- package/dist/components/portfolio-nav.cjs +2 -2
- package/dist/components/portfolio-nav.cjs.map +1 -1
- package/dist/components/portfolio-nav.js +2 -2
- package/dist/components/portfolio-nav.js.map +1 -1
- package/dist/components/rewards/journey-path.cjs +2 -2
- package/dist/components/rewards/journey-path.cjs.map +1 -1
- package/dist/components/rewards/journey-path.js +2 -2
- package/dist/components/rewards/journey-path.js.map +1 -1
- package/dist/components/rewards/leaderboard-table.cjs +1 -1
- package/dist/components/rewards/leaderboard-table.cjs.map +1 -1
- package/dist/components/rewards/leaderboard-table.js +1 -1
- package/dist/components/rewards/leaderboard-table.js.map +1 -1
- package/dist/components/rewards/level-badge.cjs +1 -1
- package/dist/components/rewards/level-badge.cjs.map +1 -1
- package/dist/components/rewards/level-badge.js +1 -1
- package/dist/components/rewards/level-badge.js.map +1 -1
- package/dist/components/rewards/level-ladder.cjs +2 -2
- package/dist/components/rewards/level-ladder.cjs.map +1 -1
- package/dist/components/rewards/level-ladder.js +2 -2
- package/dist/components/rewards/level-ladder.js.map +1 -1
- package/dist/components/scroll-section.cjs +1 -1
- package/dist/components/scroll-section.cjs.map +1 -1
- package/dist/components/scroll-section.js +1 -1
- package/dist/components/scroll-section.js.map +1 -1
- package/dist/components/share-button.cjs +1 -1
- package/dist/components/share-button.cjs.map +1 -1
- package/dist/components/share-button.js +1 -1
- package/dist/components/share-button.js.map +1 -1
- package/dist/components/stat-tile.cjs +3 -3
- package/dist/components/stat-tile.cjs.map +1 -1
- package/dist/components/stat-tile.js +3 -3
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/token-card.cjs +4 -4
- package/dist/components/token-card.cjs.map +1 -1
- package/dist/components/token-card.js +4 -4
- package/dist/components/token-card.js.map +1 -1
- package/dist/data/brand.cjs +0 -14
- package/dist/data/brand.cjs.map +1 -1
- package/dist/data/brand.d.cts +0 -14
- package/dist/data/brand.d.ts +0 -14
- package/dist/data/brand.js +0 -14
- package/dist/data/brand.js.map +1 -1
- package/dist/medialane.css +4 -0
- package/dist/preset/tailwind.cjs +16 -8
- package/dist/preset/tailwind.cjs.map +1 -1
- package/dist/preset/tailwind.js +16 -8
- package/dist/preset/tailwind.js.map +1 -1
- package/package.json +6 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }: ListingCardProps) {\n const order = rawOrder as OrderWithAnimation;\n const [imgError, setImgError] = useState(false);\n const isListing = order.offer.itemType === \"ERC721\" || order.offer.itemType === \"ERC1155\";\n const name = order.token?.name ?? `Token #${order.nftTokenId}`;\n const image = imageUrl !== undefined ? imageUrl : (order.token?.image ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\n const animationUrl = order.token?.animationUrl ?? null;\n const live = !!(order.chain && order.nftContract) && isLivingRenderCollection(order.chain.toUpperCase() as Chain, order.nftContract!);\n const assetHref = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\n if (compact) {\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 33vw, 20vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-[11px] font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n <p className=\"text-[10px] text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\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, 25vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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 font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-[15px] truncate leading-snug\">{name}</p>\n <p className=\"text-[11px] text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-[10px] uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n </div>\n <p className=\"text-[10px] text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\n )}\n\n {showActionBar && (\n <div className=\"flex items-center gap-1.5\">\n {isListing ? (\n primaryAction ? (\n primaryAction\n ) : onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-9\">\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={(e) => { e.preventDefault(); onBuy(order); }}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" /> Buy\n </button>\n </div>\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View\n </Link>\n )\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View asset\n </Link>\n )}\n {onCart && (\n <button\n className={cn(\n \"h-9 w-9 shrink-0 rounded-[9px] border flex items-center justify-center transition-colors\",\n inCart\n ? \"border-brand-orange/50 bg-brand-orange/10 text-brand-orange\"\n : \"border-border bg-background hover:bg-muted text-foreground\"\n )}\n onClick={(e) => { e.preventDefault(); if (!inCart) onCart(order); }}\n disabled={inCart}\n aria-label={inCart ? \"Added to cart\" : \"Add to cart\"}\n >\n {inCart ? <Check className=\"h-3.5 w-3.5\" /> : <ShoppingCart className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n {overflowMenu}\n </div>\n )}\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function ListingCardSkeleton() {\n return (\n <div className=\"card-base\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3 space-y-2.5\">\n <div className=\"space-y-1\">\n <div className=\"h-4 w-3/4 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-6 w-1/2 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-2/3 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }: ListingCardProps) {\n const order = rawOrder as OrderWithAnimation;\n const [imgError, setImgError] = useState(false);\n const isListing = order.offer.itemType === \"ERC721\" || order.offer.itemType === \"ERC1155\";\n const name = order.token?.name ?? `Token #${order.nftTokenId}`;\n const image = imageUrl !== undefined ? imageUrl : (order.token?.image ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\n const animationUrl = order.token?.animationUrl ?? null;\n const live = !!(order.chain && order.nftContract) && isLivingRenderCollection(order.chain.toUpperCase() as Chain, order.nftContract!);\n const assetHref = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\n if (compact) {\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 33vw, 20vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\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, 25vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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 font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-base truncate leading-snug\">{name}</p>\n <p className=\"text-2xs text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-2xs uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n </div>\n <p className=\"text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\n )}\n\n {showActionBar && (\n <div className=\"flex items-center gap-1.5\">\n {isListing ? (\n primaryAction ? (\n primaryAction\n ) : onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-9\">\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={(e) => { e.preventDefault(); onBuy(order); }}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" /> Buy\n </button>\n </div>\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View\n </Link>\n )\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View asset\n </Link>\n )}\n {onCart && (\n <button\n className={cn(\n \"min-h-11 min-w-11 shrink-0 rounded-[9px] border flex items-center justify-center transition-colors\",\n inCart\n ? \"border-brand-orange/50 bg-brand-orange/10 text-brand-orange\"\n : \"border-border bg-background hover:bg-muted text-foreground\"\n )}\n onClick={(e) => { e.preventDefault(); if (!inCart) onCart(order); }}\n disabled={inCart}\n aria-label={inCart ? \"Added to cart\" : \"Add to cart\"}\n >\n {inCart ? <Check className=\"h-3.5 w-3.5\" /> : <ShoppingCart className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n {overflowMenu}\n </div>\n )}\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function ListingCardSkeleton() {\n return (\n <div className=\"card-base\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3 space-y-2.5\">\n <div className=\"space-y-1\">\n <div className=\"h-4 w-3/4 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-6 w-1/2 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-2/3 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
@@ -43,11 +43,11 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
|
|
|
43
43
|
) }),
|
|
44
44
|
/* @__PURE__ */ jsxs("div", { className: "p-2.5 space-y-0.5", children: [
|
|
45
45
|
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold truncate", children: name }),
|
|
46
|
-
order.price?.formatted && /* @__PURE__ */ jsxs("p", { className: "text-
|
|
46
|
+
order.price?.formatted && /* @__PURE__ */ jsxs("p", { className: "text-2xs font-bold price-value inline-flex items-center gap-1", children: [
|
|
47
47
|
order.price.currency && /* @__PURE__ */ jsx(CurrencyIcon, { symbol: order.price.currency, size: 11 }),
|
|
48
48
|
formatDisplayPrice(order.price.formatted)
|
|
49
49
|
] }),
|
|
50
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
50
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground", children: timeAgo(order.createdAt) })
|
|
51
51
|
] })
|
|
52
52
|
] }) });
|
|
53
53
|
}
|
|
@@ -72,19 +72,19 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
|
|
|
72
72
|
) }),
|
|
73
73
|
/* @__PURE__ */ jsxs("div", { className: "p-3.5 space-y-3", children: [
|
|
74
74
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
75
|
-
/* @__PURE__ */ jsx("p", { className: "font-semibold text-
|
|
76
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
75
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-base truncate leading-snug", children: name }),
|
|
76
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground truncate leading-snug mt-0.5", children: order.token?.description ? order.token.description : `#${order.nftTokenId}` })
|
|
77
77
|
] }),
|
|
78
78
|
order.price?.formatted && /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-2", children: [
|
|
79
79
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
80
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
80
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs uppercase tracking-widest text-muted-foreground/55 leading-none", children: isListing ? "Price" : "Offer" }),
|
|
81
81
|
/* @__PURE__ */ jsxs("p", { className: "text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5", children: [
|
|
82
82
|
order.price.currency && /* @__PURE__ */ jsx(CurrencyIcon, { symbol: order.price.currency, size: 18 }),
|
|
83
83
|
formatDisplayPrice(order.price.formatted),
|
|
84
84
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: order.price.currency })
|
|
85
85
|
] })
|
|
86
86
|
] }),
|
|
87
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
87
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0", children: timeAgo(order.createdAt) })
|
|
88
88
|
] }),
|
|
89
89
|
showActionBar && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
90
90
|
isListing ? primaryAction ? primaryAction : onBuy ? /* @__PURE__ */ jsx("div", { className: "btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-9", children: /* @__PURE__ */ jsxs(
|
|
@@ -125,7 +125,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
|
|
|
125
125
|
"button",
|
|
126
126
|
{
|
|
127
127
|
className: cn(
|
|
128
|
-
"h-
|
|
128
|
+
"min-h-11 min-w-11 shrink-0 rounded-[9px] border flex items-center justify-center transition-colors",
|
|
129
129
|
inCart ? "border-brand-orange/50 bg-brand-orange/10 text-brand-orange" : "border-border bg-background hover:bg-muted text-foreground"
|
|
130
130
|
),
|
|
131
131
|
onClick: (e) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }: ListingCardProps) {\n const order = rawOrder as OrderWithAnimation;\n const [imgError, setImgError] = useState(false);\n const isListing = order.offer.itemType === \"ERC721\" || order.offer.itemType === \"ERC1155\";\n const name = order.token?.name ?? `Token #${order.nftTokenId}`;\n const image = imageUrl !== undefined ? imageUrl : (order.token?.image ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\n const animationUrl = order.token?.animationUrl ?? null;\n const live = !!(order.chain && order.nftContract) && isLivingRenderCollection(order.chain.toUpperCase() as Chain, order.nftContract!);\n const assetHref = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\n if (compact) {\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 33vw, 20vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-[11px] font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n <p className=\"text-[10px] text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\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, 25vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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 font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-[15px] truncate leading-snug\">{name}</p>\n <p className=\"text-[11px] text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-[10px] uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n </div>\n <p className=\"text-[10px] text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\n )}\n\n {showActionBar && (\n <div className=\"flex items-center gap-1.5\">\n {isListing ? (\n primaryAction ? (\n primaryAction\n ) : onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-9\">\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={(e) => { e.preventDefault(); onBuy(order); }}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" /> Buy\n </button>\n </div>\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View\n </Link>\n )\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View asset\n </Link>\n )}\n {onCart && (\n <button\n className={cn(\n \"h-9 w-9 shrink-0 rounded-[9px] border flex items-center justify-center transition-colors\",\n inCart\n ? \"border-brand-orange/50 bg-brand-orange/10 text-brand-orange\"\n : \"border-border bg-background hover:bg-muted text-foreground\"\n )}\n onClick={(e) => { e.preventDefault(); if (!inCart) onCart(order); }}\n disabled={inCart}\n aria-label={inCart ? \"Added to cart\" : \"Add to cart\"}\n >\n {inCart ? <Check className=\"h-3.5 w-3.5\" /> : <ShoppingCart className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n {overflowMenu}\n </div>\n )}\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function ListingCardSkeleton() {\n return (\n <div className=\"card-base\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3 space-y-2.5\">\n <div className=\"space-y-1\">\n <div className=\"h-4 w-3/4 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-6 w-1/2 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-2/3 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n}\n"],"mappings":";AA2EgB,cACE,YADF;AAzEhB,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,cAAc,OAAO,KAAK,oBAAoB;AACvD,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,UAAU;AACnB,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC,SAAS,eAAe;AACxB,SAAS,gCAAgC;AA8BlC,SAAS,YAAY,EAAE,OAAO,UAAU,SAAS,OAAO,OAAO,QAAQ,cAAc,eAAe,UAAU,OAAO,SAAS,GAAqB;AACxJ,QAAM,QAAQ;AACd,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,YAAY,MAAM,MAAM,aAAa,YAAY,MAAM,MAAM,aAAa;AAChF,QAAM,OAAO,MAAM,OAAO,QAAQ,UAAU,MAAM,UAAU;AAC5D,QAAM,QAAQ,aAAa,SAAY,WAAY,MAAM,OAAO,QAAQ,WAAW,MAAM,MAAM,KAAK,IAAI;AAGxG,QAAM,eAAe,MAAM,OAAO,gBAAgB;AAClD,QAAM,OAAO,CAAC,EAAE,MAAM,SAAS,MAAM,gBAAgB,yBAAyB,MAAM,MAAM,YAAY,GAAY,MAAM,WAAY;AACpI,QAAM,YAAY,UAAU,MAAM,WAAW,IAAI,MAAM,UAAU;AAIjE,QAAM,gBAAgB,CAAC,EAAE,SAAS,UAAU,gBAAgB;AAG5D,MAAI,SAAS;AACX,WACE,oBAAC,cAAW,WAAU,aACpB,+BAAC,QAAK,MAAM,WAAW,WAAU,SAC/B;AAAA,0BAAC,SAAI,WAAU,mDACb;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK;AAAA,UACL,MAAK;AAAA,UACL,OAAM;AAAA,UACN,WAAU;AAAA,UACV;AAAA,UACA,cAAc,MAAM,YAAY,IAAI;AAAA,UACpC,UACE,oBAAC,SAAI,WAAU,6GACb,+BAAC,UAAK,WAAU,2CAA0C;AAAA;AAAA,YAAE,MAAM;AAAA,aAAW,GAC/E;AAAA;AAAA,MAEJ,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,qBACb;AAAA,4BAAC,OAAE,WAAU,kCAAkC,gBAAK;AAAA,QACnD,MAAM,OAAO,aACZ,qBAAC,OAAE,WAAU,oEACV;AAAA,gBAAM,MAAM,YAAY,oBAAC,gBAAa,QAAQ,MAAM,MAAM,UAAU,MAAM,IAAI;AAAA,UAC9E,mBAAmB,MAAM,MAAM,SAAS;AAAA,WAC3C;AAAA,QAEF,oBAAC,OAAE,WAAU,qCAAqC,kBAAQ,MAAM,SAAS,GAAE;AAAA,SAC7E;AAAA,OACF,GACF;AAAA,EAEJ;AAGA,SACE,oBAAC,cAAW,WAAU,aACpB,+BAAC,QAAK,MAAM,WAAW,WAAU,SAC/B;AAAA,wBAAC,SAAI,WAAU,mDACb;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK;AAAA,QACL,MAAK;AAAA,QACL,OAAM;AAAA,QACN,WAAU;AAAA,QACV;AAAA,QACA,cAAc,MAAM,YAAY,IAAI;AAAA,QACpC,UACE,oBAAC,SAAI,WAAU,6GACb,+BAAC,UAAK,WAAU,4CAA2C;AAAA;AAAA,UAAE,MAAM;AAAA,WAAW,GAChF;AAAA;AAAA,IAEJ,GACF;AAAA,IAEA,qBAAC,SAAI,WAAU,mBACb;AAAA,2BAAC,SAAI,WAAU,WACb;AAAA,4BAAC,OAAE,WAAU,mDAAmD,gBAAK;AAAA,QACrE,oBAAC,OAAE,WAAU,kEACV,gBAAM,OAAO,cAAc,MAAM,MAAM,cAAc,IAAI,MAAM,UAAU,IAC5E;AAAA,SACF;AAAA,MAGC,MAAM,OAAO,aACZ,qBAAC,SAAI,WAAU,wCACb;AAAA,6BAAC,SAAI,WAAU,WACb;AAAA,8BAAC,OAAE,WAAU,+EACV,sBAAY,UAAU,SACzB;AAAA,UACA,qBAAC,OAAE,WAAU,sFACV;AAAA,kBAAM,MAAM,YAAY,oBAAC,gBAAa,QAAQ,MAAM,MAAM,UAAU,MAAM,IAAI;AAAA,YAC9E,mBAAmB,MAAM,MAAM,SAAS;AAAA,YACzC,oBAAC,UAAK,WAAU,WAAW,gBAAM,MAAM,UAAS;AAAA,aAClD;AAAA,WACF;AAAA,QACA,oBAAC,OAAE,WAAU,mEACV,kBAAQ,MAAM,SAAS,GAC1B;AAAA,SACF;AAAA,MAGD,iBACC,qBAAC,SAAI,WAAU,6BACZ;AAAA,oBACC,gBACE,gBACE,QACF,oBAAC,SAAI,WAAU,2DACb;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,SAAS,CAAC,MAAM;AAAE,gBAAE,eAAe;AAAG,oBAAM,KAAK;AAAA,YAAG;AAAA,YAEpD;AAAA,kCAAC,OAAI,WAAU,wBAAuB;AAAA,cAAE;AAAA;AAAA;AAAA,QAC1C,GACF,IAEA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,WAAU;AAAA,YAEV;AAAA,kCAAC,gBAAa,WAAU,wBAAuB;AAAA,cAAE;AAAA;AAAA;AAAA,QACnD,IAGF;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,WAAU;AAAA,YAEV;AAAA,kCAAC,gBAAa,WAAU,wBAAuB;AAAA,cAAE;AAAA;AAAA;AAAA,QACnD;AAAA,QAED,UACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,SACI,gEACA;AAAA,YACN;AAAA,YACA,SAAS,CAAC,MAAM;AAAE,gBAAE,eAAe;AAAG,kBAAI,CAAC,OAAQ,QAAO,KAAK;AAAA,YAAG;AAAA,YAClE,UAAU;AAAA,YACV,cAAY,SAAS,kBAAkB;AAAA,YAEtC,mBAAS,oBAAC,SAAM,WAAU,eAAc,IAAK,oBAAC,gBAAa,WAAU,eAAc;AAAA;AAAA,QACtF;AAAA,QAED;AAAA,SACH;AAAA,OAEJ;AAAA,KACF,GACF;AAEJ;AAEO,SAAS,sBAAsB;AACpC,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,SAAI,WAAU,+CAA8C;AAAA,IAC7D,qBAAC,SAAI,WAAU,mBACb;AAAA,2BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,SAAI,WAAU,4CAA2C;AAAA,QAC1D,oBAAC,SAAI,WAAU,4CAA2C;AAAA,SAC5D;AAAA,MACA,oBAAC,SAAI,WAAU,4CAA2C;AAAA,MAC1D,oBAAC,SAAI,WAAU,4CAA2C;AAAA,OAC5D;AAAA,KACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }: ListingCardProps) {\n const order = rawOrder as OrderWithAnimation;\n const [imgError, setImgError] = useState(false);\n const isListing = order.offer.itemType === \"ERC721\" || order.offer.itemType === \"ERC1155\";\n const name = order.token?.name ?? `Token #${order.nftTokenId}`;\n const image = imageUrl !== undefined ? imageUrl : (order.token?.image ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\n const animationUrl = order.token?.animationUrl ?? null;\n const live = !!(order.chain && order.nftContract) && isLivingRenderCollection(order.chain.toUpperCase() as Chain, order.nftContract!);\n const assetHref = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\n if (compact) {\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 33vw, 20vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\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, 25vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\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 font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-base truncate leading-snug\">{name}</p>\n <p className=\"text-2xs text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-2xs uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n </div>\n <p className=\"text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\n )}\n\n {showActionBar && (\n <div className=\"flex items-center gap-1.5\">\n {isListing ? (\n primaryAction ? (\n primaryAction\n ) : onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-9\">\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={(e) => { e.preventDefault(); onBuy(order); }}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" /> Buy\n </button>\n </div>\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View\n </Link>\n )\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View asset\n </Link>\n )}\n {onCart && (\n <button\n className={cn(\n \"min-h-11 min-w-11 shrink-0 rounded-[9px] border flex items-center justify-center transition-colors\",\n inCart\n ? \"border-brand-orange/50 bg-brand-orange/10 text-brand-orange\"\n : \"border-border bg-background hover:bg-muted text-foreground\"\n )}\n onClick={(e) => { e.preventDefault(); if (!inCart) onCart(order); }}\n disabled={inCart}\n aria-label={inCart ? \"Added to cart\" : \"Add to cart\"}\n >\n {inCart ? <Check className=\"h-3.5 w-3.5\" /> : <ShoppingCart className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n {overflowMenu}\n </div>\n )}\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function ListingCardSkeleton() {\n return (\n <div className=\"card-base\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3 space-y-2.5\">\n <div className=\"space-y-1\">\n <div className=\"h-4 w-3/4 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-6 w-1/2 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-2/3 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -103,7 +103,7 @@ function MedialaneCollectionCard({
|
|
|
103
103
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative px-4 pt-3 pb-4", children: [
|
|
104
104
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start gap-2", children: [
|
|
105
105
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "min-w-0 flex-1 text-[18px] font-bold leading-snug truncate", children: displayName }),
|
|
106
|
-
serial && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-
|
|
106
|
+
serial && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-2xs font-semibold text-[#0a0e1f]/80 dark:text-white/80 tabular-nums", children: serial })
|
|
107
107
|
] }),
|
|
108
108
|
collection && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-0.5 text-[12.5px] text-[#0a0e1f]/55 dark:text-white/55 truncate", children: collection }),
|
|
109
109
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "my-3 h-px bg-black/10 dark:bg-white/10" }),
|
|
@@ -117,7 +117,7 @@ function MedialaneCollectionCard({
|
|
|
117
117
|
}
|
|
118
118
|
),
|
|
119
119
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[9px] font-bold tracking-[0.18em] text-[#0a0e1f]/50 dark:text-white/50", children: "MEDIALANE" }),
|
|
120
|
-
creator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ml-auto min-w-0 truncate text-
|
|
120
|
+
creator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums", children: creator })
|
|
121
121
|
] })
|
|
122
122
|
] })
|
|
123
123
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef } from \"react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n /** Resolved, browser-loadable image URL. Empty → brand-gradient placeholder. */\n image?: string | null;\n /** Asset or collection name. */\n name?: string;\n /** Secondary line under the name (collection name, symbol, ticker…). */\n collection?: string;\n /** Edition or serial shown as a pill, e.g. \"#42\" or \"1 / 300\". */\n serial?: string;\n /** Creator display name or shortened address. */\n creator?: string;\n className?: string;\n}\n\nconst FRAME_GRADIENT =\n \"linear-gradient(135deg, #3b7bff, #8a5cf6 38%, #f6608f 70%, #fb8b46)\";\n\n/** The Medialane Collection Card — a branded collectors-card preview of an\n * asset or collection. A self-contained collectible object: brand-spectrum\n * frame, inset artwork on foil-tinted material, pointer-tracked 3D tilt +\n * holographic sheen, display-face name, serial pill, Medialane maker's mark.\n * Follows the app's light/dark theme automatically (`.ml-card-material` /\n * `.ml-card-sheen` in medialane.css carry the `.dark` overrides — light is\n * the default, no `.dark` ancestor needed). Fluid width — size it with the\n * parent container. Pure presentation. */\nexport function MedialaneCollectionCard({\n image,\n name,\n collection,\n serial,\n creator,\n className,\n}: MedialaneCollectionCardProps) {\n const frameRef = useRef<HTMLDivElement>(null);\n\n const handleMove = (e: React.PointerEvent<HTMLDivElement>) => {\n const el = frameRef.current;\n if (!el || e.pointerType !== \"mouse\") return;\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n const rect = el.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = (e.clientY - rect.top) / rect.height;\n el.style.setProperty(\"--rx\", `${(0.5 - y) * 9}deg`);\n el.style.setProperty(\"--ry\", `${(x - 0.5) * 9}deg`);\n el.style.setProperty(\"--mx\", `${x * 100}%`);\n el.style.setProperty(\"--my\", `${y * 100}%`);\n el.style.setProperty(\"--pop\", \"1.015\");\n };\n\n const handleLeave = () => {\n const el = frameRef.current;\n if (!el) return;\n el.style.setProperty(\"--rx\", \"0deg\");\n el.style.setProperty(\"--ry\", \"0deg\");\n el.style.setProperty(\"--pop\", \"1\");\n };\n\n const displayName = name?.trim() || \"Untitled\";\n\n return (\n <div className={cn(\"group\", className)} style={{ perspective: \"1000px\" }}>\n <div\n ref={frameRef}\n onPointerMove={handleMove}\n onPointerLeave={handleLeave}\n className=\"rounded-[24px] p-[1.5px] transition-transform duration-200 ease-out will-change-transform shadow-[0_16px_40px_-16px_rgba(91,76,230,0.45)]\"\n style={{\n background: FRAME_GRADIENT,\n transform:\n \"rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) scale(var(--pop, 1))\",\n transformStyle: \"preserve-3d\",\n }}\n >\n <div\n className=\"ml-card-material relative rounded-[22.5px] overflow-hidden text-[#0a0e1f] dark:text-white ring-1 ring-inset ring-black/[0.06] dark:ring-white/[0.06]\"\n >\n {/* Artwork — inset like a printed trading card */}\n <div className=\"p-2.5 pb-0\">\n <div className=\"relative aspect-[4/5] w-full overflow-hidden rounded-[16px] ring-1 ring-black/10 dark:ring-white/10\">\n {image ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={image} alt={displayName} className=\"h-full w-full object-cover\" />\n ) : (\n <div\n className=\"h-full w-full\"\n style={{ background: FRAME_GRADIENT, opacity: 0.9 }}\n />\n )}\n {/* Bottom scrim for depth against the meta block */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-16\"\n style={{ background: \"linear-gradient(to top, rgba(10,14,31,0.45), transparent)\" }}\n />\n </div>\n </div>\n\n {/* Holographic sheen — visible at rest, blooms and tracks on hover */}\n <div\n aria-hidden\n className=\"ml-card-sheen pointer-events-none absolute inset-0 opacity-40 group-hover:opacity-100 transition-opacity duration-300\"\n />\n\n {/* Meta */}\n <div className=\"relative px-4 pt-3 pb-4\">\n <div className=\"flex items-start gap-2\">\n <p className=\"min-w-0 flex-1 text-[18px] font-bold leading-snug truncate\">\n {displayName}\n </p>\n {serial && (\n <span className=\"mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef } from \"react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n /** Resolved, browser-loadable image URL. Empty → brand-gradient placeholder. */\n image?: string | null;\n /** Asset or collection name. */\n name?: string;\n /** Secondary line under the name (collection name, symbol, ticker…). */\n collection?: string;\n /** Edition or serial shown as a pill, e.g. \"#42\" or \"1 / 300\". */\n serial?: string;\n /** Creator display name or shortened address. */\n creator?: string;\n className?: string;\n}\n\nconst FRAME_GRADIENT =\n \"linear-gradient(135deg, #3b7bff, #8a5cf6 38%, #f6608f 70%, #fb8b46)\";\n\n/** The Medialane Collection Card — a branded collectors-card preview of an\n * asset or collection. A self-contained collectible object: brand-spectrum\n * frame, inset artwork on foil-tinted material, pointer-tracked 3D tilt +\n * holographic sheen, display-face name, serial pill, Medialane maker's mark.\n * Follows the app's light/dark theme automatically (`.ml-card-material` /\n * `.ml-card-sheen` in medialane.css carry the `.dark` overrides — light is\n * the default, no `.dark` ancestor needed). Fluid width — size it with the\n * parent container. Pure presentation. */\nexport function MedialaneCollectionCard({\n image,\n name,\n collection,\n serial,\n creator,\n className,\n}: MedialaneCollectionCardProps) {\n const frameRef = useRef<HTMLDivElement>(null);\n\n const handleMove = (e: React.PointerEvent<HTMLDivElement>) => {\n const el = frameRef.current;\n if (!el || e.pointerType !== \"mouse\") return;\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n const rect = el.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = (e.clientY - rect.top) / rect.height;\n el.style.setProperty(\"--rx\", `${(0.5 - y) * 9}deg`);\n el.style.setProperty(\"--ry\", `${(x - 0.5) * 9}deg`);\n el.style.setProperty(\"--mx\", `${x * 100}%`);\n el.style.setProperty(\"--my\", `${y * 100}%`);\n el.style.setProperty(\"--pop\", \"1.015\");\n };\n\n const handleLeave = () => {\n const el = frameRef.current;\n if (!el) return;\n el.style.setProperty(\"--rx\", \"0deg\");\n el.style.setProperty(\"--ry\", \"0deg\");\n el.style.setProperty(\"--pop\", \"1\");\n };\n\n const displayName = name?.trim() || \"Untitled\";\n\n return (\n <div className={cn(\"group\", className)} style={{ perspective: \"1000px\" }}>\n <div\n ref={frameRef}\n onPointerMove={handleMove}\n onPointerLeave={handleLeave}\n className=\"rounded-[24px] p-[1.5px] transition-transform duration-200 ease-out will-change-transform shadow-[0_16px_40px_-16px_rgba(91,76,230,0.45)]\"\n style={{\n background: FRAME_GRADIENT,\n transform:\n \"rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) scale(var(--pop, 1))\",\n transformStyle: \"preserve-3d\",\n }}\n >\n <div\n className=\"ml-card-material relative rounded-[22.5px] overflow-hidden text-[#0a0e1f] dark:text-white ring-1 ring-inset ring-black/[0.06] dark:ring-white/[0.06]\"\n >\n {/* Artwork — inset like a printed trading card */}\n <div className=\"p-2.5 pb-0\">\n <div className=\"relative aspect-[4/5] w-full overflow-hidden rounded-[16px] ring-1 ring-black/10 dark:ring-white/10\">\n {image ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={image} alt={displayName} className=\"h-full w-full object-cover\" />\n ) : (\n <div\n className=\"h-full w-full\"\n style={{ background: FRAME_GRADIENT, opacity: 0.9 }}\n />\n )}\n {/* Bottom scrim for depth against the meta block */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-16\"\n style={{ background: \"linear-gradient(to top, rgba(10,14,31,0.45), transparent)\" }}\n />\n </div>\n </div>\n\n {/* Holographic sheen — visible at rest, blooms and tracks on hover */}\n <div\n aria-hidden\n className=\"ml-card-sheen pointer-events-none absolute inset-0 opacity-40 group-hover:opacity-100 transition-opacity duration-300\"\n />\n\n {/* Meta */}\n <div className=\"relative px-4 pt-3 pb-4\">\n <div className=\"flex items-start gap-2\">\n <p className=\"min-w-0 flex-1 text-[18px] font-bold leading-snug truncate\">\n {displayName}\n </p>\n {serial && (\n <span className=\"mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-2xs font-semibold text-[#0a0e1f]/80 dark:text-white/80 tabular-nums\">\n {serial}\n </span>\n )}\n </div>\n {collection && (\n <p className=\"mt-0.5 text-[12.5px] text-[#0a0e1f]/55 dark:text-white/55 truncate\">{collection}</p>\n )}\n <div className=\"my-3 h-px bg-black/10 dark:bg-white/10\" />\n <div className=\"flex items-center gap-2\">\n <span\n aria-hidden\n className=\"h-3.5 w-3.5 rounded-full shrink-0\"\n style={{ background: FRAME_GRADIENT }}\n />\n <span className=\"text-[9px] font-bold tracking-[0.18em] text-[#0a0e1f]/50 dark:text-white/50\">\n MEDIALANE\n </span>\n {creator && (\n <span className=\"ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums\">\n {creator}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -80,7 +80,7 @@ function MedialaneCollectionCard({
|
|
|
80
80
|
/* @__PURE__ */ jsxs("div", { className: "relative px-4 pt-3 pb-4", children: [
|
|
81
81
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
82
82
|
/* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 text-[18px] font-bold leading-snug truncate", children: displayName }),
|
|
83
|
-
serial && /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-
|
|
83
|
+
serial && /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-2xs font-semibold text-[#0a0e1f]/80 dark:text-white/80 tabular-nums", children: serial })
|
|
84
84
|
] }),
|
|
85
85
|
collection && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[12.5px] text-[#0a0e1f]/55 dark:text-white/55 truncate", children: collection }),
|
|
86
86
|
/* @__PURE__ */ jsx("div", { className: "my-3 h-px bg-black/10 dark:bg-white/10" }),
|
|
@@ -94,7 +94,7 @@ function MedialaneCollectionCard({
|
|
|
94
94
|
}
|
|
95
95
|
),
|
|
96
96
|
/* @__PURE__ */ jsx("span", { className: "text-[9px] font-bold tracking-[0.18em] text-[#0a0e1f]/50 dark:text-white/50", children: "MEDIALANE" }),
|
|
97
|
-
creator && /* @__PURE__ */ jsx("span", { className: "ml-auto min-w-0 truncate text-
|
|
97
|
+
creator && /* @__PURE__ */ jsx("span", { className: "ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums", children: creator })
|
|
98
98
|
] })
|
|
99
99
|
] })
|
|
100
100
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef } from \"react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n /** Resolved, browser-loadable image URL. Empty → brand-gradient placeholder. */\n image?: string | null;\n /** Asset or collection name. */\n name?: string;\n /** Secondary line under the name (collection name, symbol, ticker…). */\n collection?: string;\n /** Edition or serial shown as a pill, e.g. \"#42\" or \"1 / 300\". */\n serial?: string;\n /** Creator display name or shortened address. */\n creator?: string;\n className?: string;\n}\n\nconst FRAME_GRADIENT =\n \"linear-gradient(135deg, #3b7bff, #8a5cf6 38%, #f6608f 70%, #fb8b46)\";\n\n/** The Medialane Collection Card — a branded collectors-card preview of an\n * asset or collection. A self-contained collectible object: brand-spectrum\n * frame, inset artwork on foil-tinted material, pointer-tracked 3D tilt +\n * holographic sheen, display-face name, serial pill, Medialane maker's mark.\n * Follows the app's light/dark theme automatically (`.ml-card-material` /\n * `.ml-card-sheen` in medialane.css carry the `.dark` overrides — light is\n * the default, no `.dark` ancestor needed). Fluid width — size it with the\n * parent container. Pure presentation. */\nexport function MedialaneCollectionCard({\n image,\n name,\n collection,\n serial,\n creator,\n className,\n}: MedialaneCollectionCardProps) {\n const frameRef = useRef<HTMLDivElement>(null);\n\n const handleMove = (e: React.PointerEvent<HTMLDivElement>) => {\n const el = frameRef.current;\n if (!el || e.pointerType !== \"mouse\") return;\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n const rect = el.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = (e.clientY - rect.top) / rect.height;\n el.style.setProperty(\"--rx\", `${(0.5 - y) * 9}deg`);\n el.style.setProperty(\"--ry\", `${(x - 0.5) * 9}deg`);\n el.style.setProperty(\"--mx\", `${x * 100}%`);\n el.style.setProperty(\"--my\", `${y * 100}%`);\n el.style.setProperty(\"--pop\", \"1.015\");\n };\n\n const handleLeave = () => {\n const el = frameRef.current;\n if (!el) return;\n el.style.setProperty(\"--rx\", \"0deg\");\n el.style.setProperty(\"--ry\", \"0deg\");\n el.style.setProperty(\"--pop\", \"1\");\n };\n\n const displayName = name?.trim() || \"Untitled\";\n\n return (\n <div className={cn(\"group\", className)} style={{ perspective: \"1000px\" }}>\n <div\n ref={frameRef}\n onPointerMove={handleMove}\n onPointerLeave={handleLeave}\n className=\"rounded-[24px] p-[1.5px] transition-transform duration-200 ease-out will-change-transform shadow-[0_16px_40px_-16px_rgba(91,76,230,0.45)]\"\n style={{\n background: FRAME_GRADIENT,\n transform:\n \"rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) scale(var(--pop, 1))\",\n transformStyle: \"preserve-3d\",\n }}\n >\n <div\n className=\"ml-card-material relative rounded-[22.5px] overflow-hidden text-[#0a0e1f] dark:text-white ring-1 ring-inset ring-black/[0.06] dark:ring-white/[0.06]\"\n >\n {/* Artwork — inset like a printed trading card */}\n <div className=\"p-2.5 pb-0\">\n <div className=\"relative aspect-[4/5] w-full overflow-hidden rounded-[16px] ring-1 ring-black/10 dark:ring-white/10\">\n {image ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={image} alt={displayName} className=\"h-full w-full object-cover\" />\n ) : (\n <div\n className=\"h-full w-full\"\n style={{ background: FRAME_GRADIENT, opacity: 0.9 }}\n />\n )}\n {/* Bottom scrim for depth against the meta block */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-16\"\n style={{ background: \"linear-gradient(to top, rgba(10,14,31,0.45), transparent)\" }}\n />\n </div>\n </div>\n\n {/* Holographic sheen — visible at rest, blooms and tracks on hover */}\n <div\n aria-hidden\n className=\"ml-card-sheen pointer-events-none absolute inset-0 opacity-40 group-hover:opacity-100 transition-opacity duration-300\"\n />\n\n {/* Meta */}\n <div className=\"relative px-4 pt-3 pb-4\">\n <div className=\"flex items-start gap-2\">\n <p className=\"min-w-0 flex-1 text-[18px] font-bold leading-snug truncate\">\n {displayName}\n </p>\n {serial && (\n <span className=\"mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef } from \"react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n /** Resolved, browser-loadable image URL. Empty → brand-gradient placeholder. */\n image?: string | null;\n /** Asset or collection name. */\n name?: string;\n /** Secondary line under the name (collection name, symbol, ticker…). */\n collection?: string;\n /** Edition or serial shown as a pill, e.g. \"#42\" or \"1 / 300\". */\n serial?: string;\n /** Creator display name or shortened address. */\n creator?: string;\n className?: string;\n}\n\nconst FRAME_GRADIENT =\n \"linear-gradient(135deg, #3b7bff, #8a5cf6 38%, #f6608f 70%, #fb8b46)\";\n\n/** The Medialane Collection Card — a branded collectors-card preview of an\n * asset or collection. A self-contained collectible object: brand-spectrum\n * frame, inset artwork on foil-tinted material, pointer-tracked 3D tilt +\n * holographic sheen, display-face name, serial pill, Medialane maker's mark.\n * Follows the app's light/dark theme automatically (`.ml-card-material` /\n * `.ml-card-sheen` in medialane.css carry the `.dark` overrides — light is\n * the default, no `.dark` ancestor needed). Fluid width — size it with the\n * parent container. Pure presentation. */\nexport function MedialaneCollectionCard({\n image,\n name,\n collection,\n serial,\n creator,\n className,\n}: MedialaneCollectionCardProps) {\n const frameRef = useRef<HTMLDivElement>(null);\n\n const handleMove = (e: React.PointerEvent<HTMLDivElement>) => {\n const el = frameRef.current;\n if (!el || e.pointerType !== \"mouse\") return;\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n const rect = el.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = (e.clientY - rect.top) / rect.height;\n el.style.setProperty(\"--rx\", `${(0.5 - y) * 9}deg`);\n el.style.setProperty(\"--ry\", `${(x - 0.5) * 9}deg`);\n el.style.setProperty(\"--mx\", `${x * 100}%`);\n el.style.setProperty(\"--my\", `${y * 100}%`);\n el.style.setProperty(\"--pop\", \"1.015\");\n };\n\n const handleLeave = () => {\n const el = frameRef.current;\n if (!el) return;\n el.style.setProperty(\"--rx\", \"0deg\");\n el.style.setProperty(\"--ry\", \"0deg\");\n el.style.setProperty(\"--pop\", \"1\");\n };\n\n const displayName = name?.trim() || \"Untitled\";\n\n return (\n <div className={cn(\"group\", className)} style={{ perspective: \"1000px\" }}>\n <div\n ref={frameRef}\n onPointerMove={handleMove}\n onPointerLeave={handleLeave}\n className=\"rounded-[24px] p-[1.5px] transition-transform duration-200 ease-out will-change-transform shadow-[0_16px_40px_-16px_rgba(91,76,230,0.45)]\"\n style={{\n background: FRAME_GRADIENT,\n transform:\n \"rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) scale(var(--pop, 1))\",\n transformStyle: \"preserve-3d\",\n }}\n >\n <div\n className=\"ml-card-material relative rounded-[22.5px] overflow-hidden text-[#0a0e1f] dark:text-white ring-1 ring-inset ring-black/[0.06] dark:ring-white/[0.06]\"\n >\n {/* Artwork — inset like a printed trading card */}\n <div className=\"p-2.5 pb-0\">\n <div className=\"relative aspect-[4/5] w-full overflow-hidden rounded-[16px] ring-1 ring-black/10 dark:ring-white/10\">\n {image ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={image} alt={displayName} className=\"h-full w-full object-cover\" />\n ) : (\n <div\n className=\"h-full w-full\"\n style={{ background: FRAME_GRADIENT, opacity: 0.9 }}\n />\n )}\n {/* Bottom scrim for depth against the meta block */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-16\"\n style={{ background: \"linear-gradient(to top, rgba(10,14,31,0.45), transparent)\" }}\n />\n </div>\n </div>\n\n {/* Holographic sheen — visible at rest, blooms and tracks on hover */}\n <div\n aria-hidden\n className=\"ml-card-sheen pointer-events-none absolute inset-0 opacity-40 group-hover:opacity-100 transition-opacity duration-300\"\n />\n\n {/* Meta */}\n <div className=\"relative px-4 pt-3 pb-4\">\n <div className=\"flex items-start gap-2\">\n <p className=\"min-w-0 flex-1 text-[18px] font-bold leading-snug truncate\">\n {displayName}\n </p>\n {serial && (\n <span className=\"mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-2xs font-semibold text-[#0a0e1f]/80 dark:text-white/80 tabular-nums\">\n {serial}\n </span>\n )}\n </div>\n {collection && (\n <p className=\"mt-0.5 text-[12.5px] text-[#0a0e1f]/55 dark:text-white/55 truncate\">{collection}</p>\n )}\n <div className=\"my-3 h-px bg-black/10 dark:bg-white/10\" />\n <div className=\"flex items-center gap-2\">\n <span\n aria-hidden\n className=\"h-3.5 w-3.5 rounded-full shrink-0\"\n style={{ background: FRAME_GRADIENT }}\n />\n <span className=\"text-[9px] font-bold tracking-[0.18em] text-[#0a0e1f]/50 dark:text-white/50\">\n MEDIALANE\n </span>\n {creator && (\n <span className=\"ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums\">\n {creator}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -54,7 +54,7 @@ function Kbd({ children, className }) {
|
|
|
54
54
|
{
|
|
55
55
|
className: (0, import_cn.cn)(
|
|
56
56
|
"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5",
|
|
57
|
-
"font-sans text-
|
|
57
|
+
"font-sans text-2xs leading-none text-muted-foreground",
|
|
58
58
|
className
|
|
59
59
|
),
|
|
60
60
|
children
|
|
@@ -98,7 +98,7 @@ function CommandRow({ item, primary, onSelect }) {
|
|
|
98
98
|
{
|
|
99
99
|
className: (0, import_cn.cn)(
|
|
100
100
|
"block truncate leading-tight",
|
|
101
|
-
primary ? "text-
|
|
101
|
+
primary ? "text-base font-medium" : "text-sm"
|
|
102
102
|
),
|
|
103
103
|
children: item.label
|
|
104
104
|
}
|
|
@@ -182,7 +182,7 @@ function NavCommandMenu({
|
|
|
182
182
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
183
183
|
import_framer_motion.motion.div,
|
|
184
184
|
{
|
|
185
|
-
className: "fixed inset-0 z-[101] flex items-end justify-center p-3 pb-
|
|
185
|
+
className: "fixed inset-0 z-[101] flex items-end justify-center p-3 pb-[calc(1rem+env(safe-area-inset-bottom))] sm:items-center sm:p-4",
|
|
186
186
|
initial: { opacity: 0, y: 24, scale: 1 },
|
|
187
187
|
animate: { opacity: 1, y: 0, scale: 1 },
|
|
188
188
|
exit: { opacity: 0, y: 24 },
|
|
@@ -215,7 +215,7 @@ function NavCommandMenu({
|
|
|
215
215
|
value: query,
|
|
216
216
|
onValueChange: setQuery,
|
|
217
217
|
placeholder: "Search or run a command\u2026",
|
|
218
|
-
className: "flex-1 bg-transparent text-
|
|
218
|
+
className: "flex-1 bg-transparent text-base outline-none placeholder:text-muted-foreground"
|
|
219
219
|
}
|
|
220
220
|
),
|
|
221
221
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { className: "hidden sm:inline-flex", children: "esc" }),
|
|
@@ -263,7 +263,7 @@ function NavCommandMenu({
|
|
|
263
263
|
] })
|
|
264
264
|
] })
|
|
265
265
|
] }),
|
|
266
|
-
brandSlot ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex shrink-0 items-center gap-2 text-
|
|
266
|
+
brandSlot ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex shrink-0 items-center gap-2 text-2xs text-muted-foreground/50", children: [
|
|
267
267
|
"medialane",
|
|
268
268
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { className: "hidden sm:inline-flex", children: "\u2318K" })
|
|
269
269
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n /** Optional one-line description shown under the label */\n description?: string;\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (passkey wallets, injected wallet\n * connectors, Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n /**\n * Show the \"↑↓ Navigate ↵ Open\" keyboard-hint text next to `footerSlot`.\n * Defaults to `true` (unchanged behavior).\n */\n showKeyboardHints?: boolean;\n /**\n * Optional override for the trailing \"medialane ⌘K\" brandmark at the\n * footer's right edge — replaces it entirely rather than adorning it, so\n * an app can put something more actionable there (e.g. a \"Connect\" button)\n * instead of a static label. Omit to keep the default brandmark.\n */\n brandSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Small primitives ──────────────────────────────────────────────────────────\n\nfunction Kbd({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <kbd\n className={cn(\n \"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5\",\n \"font-sans text-[10px] leading-none text-muted-foreground\",\n className\n )}\n >\n {children}\n </kbd>\n );\n}\n\nfunction CommandRow({ item, primary, onSelect }: { item: NavCommand; primary: boolean; onSelect: () => void }) {\n return (\n <Command.Item\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={onSelect}\n className={cn(\n \"group/item flex cursor-pointer items-center gap-3 rounded-xl\",\n \"transition-colors duration-150 aria-selected:bg-primary/10\",\n primary ? \"px-2.5 py-2.5\" : \"px-2.5 py-2\"\n )}\n >\n <span\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg transition-colors\",\n \"bg-muted/50 group-aria-selected/item:bg-primary/15\",\n primary ? \"h-9 w-9\" : \"h-8 w-8\"\n )}\n >\n <item.icon\n className={cn(\n \"text-foreground/80 transition-colors group-aria-selected/item:text-primary\",\n primary ? \"h-[18px] w-[18px]\" : \"h-4 w-4\"\n )}\n />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n \"block truncate leading-tight\",\n primary ? \"text-[15px] font-medium\" : \"text-sm\"\n )}\n >\n {item.label}\n </span>\n {item.description && (\n <span className=\"mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground\">\n {item.description}\n </span>\n )}\n </span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70\" />\n </Command.Item>\n );\n}\n\nconst GROUP_HEADING_CLASSES = cn(\n \"[&_[cmdk-group-heading]]:px-2.5\",\n \"[&_[cmdk-group-heading]]:pt-2\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[10.5px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-[0.08em]\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/60\"\n);\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({\n commands,\n trigger,\n accountSlot,\n footerSlot,\n showKeyboardHints = true,\n brandSlot,\n}: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const [query, setQuery] = React.useState(\"\");\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n setQuery(\"\");\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel — centered on desktop, bottom sheet on mobile */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-end justify-center p-3 pb-4 sm:items-center sm:p-4\"\n initial={{ opacity: 0, y: 24, scale: 1 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className={cn(\n \"flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]\",\n \"border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\"\n )}\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\" className=\"flex min-h-0 flex-1 flex-col\">\n {/* Drag handle (mobile) */}\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n\n {/* Search bar */}\n <div className=\"flex items-center gap-3 border-b border-border/40 px-4 py-3.5\">\n <Search\n className={cn(\n \"h-[18px] w-[18px] shrink-0 transition-colors\",\n query ? \"text-primary\" : \"text-muted-foreground\"\n )}\n />\n <Command.Input\n ref={inputRef}\n value={query}\n onValueChange={setQuery}\n placeholder=\"Search or run a command…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <Kbd className=\"hidden sm:inline-flex\">esc</Kbd>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group heading={group.heading} className={GROUP_HEADING_CLASSES}>\n {group.items.map((item) => (\n <CommandRow\n key={item.id}\n item={item}\n primary={primary}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-muted/20 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2\">\n <div className=\"flex items-center gap-3\">\n {footerSlot}\n {showKeyboardHints && (\n <span className=\"hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex\">\n <span className=\"flex items-center gap-1\"><Kbd>↑</Kbd><Kbd>↓</Kbd> Navigate</span>\n <span className=\"flex items-center gap-1\"><Kbd>↵</Kbd> Open</span>\n </span>\n )}\n </div>\n {brandSlot ?? (\n <span className=\"flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50\">\n medialane\n <Kbd className=\"hidden sm:inline-flex\">⌘K</Kbd>\n </span>\n )}\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n /** Optional one-line description shown under the label */\n description?: string;\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (passkey wallets, injected wallet\n * connectors, Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n /**\n * Show the \"↑↓ Navigate ↵ Open\" keyboard-hint text next to `footerSlot`.\n * Defaults to `true` (unchanged behavior).\n */\n showKeyboardHints?: boolean;\n /**\n * Optional override for the trailing \"medialane ⌘K\" brandmark at the\n * footer's right edge — replaces it entirely rather than adorning it, so\n * an app can put something more actionable there (e.g. a \"Connect\" button)\n * instead of a static label. Omit to keep the default brandmark.\n */\n brandSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Small primitives ──────────────────────────────────────────────────────────\n\nfunction Kbd({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <kbd\n className={cn(\n \"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5\",\n \"font-sans text-2xs leading-none text-muted-foreground\",\n className\n )}\n >\n {children}\n </kbd>\n );\n}\n\nfunction CommandRow({ item, primary, onSelect }: { item: NavCommand; primary: boolean; onSelect: () => void }) {\n return (\n <Command.Item\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={onSelect}\n className={cn(\n \"group/item flex cursor-pointer items-center gap-3 rounded-xl\",\n \"transition-colors duration-150 aria-selected:bg-primary/10\",\n primary ? \"px-2.5 py-2.5\" : \"px-2.5 py-2\"\n )}\n >\n <span\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg transition-colors\",\n \"bg-muted/50 group-aria-selected/item:bg-primary/15\",\n primary ? \"h-9 w-9\" : \"h-8 w-8\"\n )}\n >\n <item.icon\n className={cn(\n \"text-foreground/80 transition-colors group-aria-selected/item:text-primary\",\n primary ? \"h-[18px] w-[18px]\" : \"h-4 w-4\"\n )}\n />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n \"block truncate leading-tight\",\n primary ? \"text-base font-medium\" : \"text-sm\"\n )}\n >\n {item.label}\n </span>\n {item.description && (\n <span className=\"mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground\">\n {item.description}\n </span>\n )}\n </span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70\" />\n </Command.Item>\n );\n}\n\nconst GROUP_HEADING_CLASSES = cn(\n \"[&_[cmdk-group-heading]]:px-2.5\",\n \"[&_[cmdk-group-heading]]:pt-2\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[10.5px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-[0.08em]\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/60\"\n);\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({\n commands,\n trigger,\n accountSlot,\n footerSlot,\n showKeyboardHints = true,\n brandSlot,\n}: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const [query, setQuery] = React.useState(\"\");\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n setQuery(\"\");\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel — centered on desktop, bottom sheet on mobile */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-end justify-center p-3 pb-[calc(1rem+env(safe-area-inset-bottom))] sm:items-center sm:p-4\"\n initial={{ opacity: 0, y: 24, scale: 1 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className={cn(\n \"flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]\",\n \"border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\"\n )}\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\" className=\"flex min-h-0 flex-1 flex-col\">\n {/* Drag handle (mobile) */}\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n\n {/* Search bar */}\n <div className=\"flex items-center gap-3 border-b border-border/40 px-4 py-3.5\">\n <Search\n className={cn(\n \"h-[18px] w-[18px] shrink-0 transition-colors\",\n query ? \"text-primary\" : \"text-muted-foreground\"\n )}\n />\n <Command.Input\n ref={inputRef}\n value={query}\n onValueChange={setQuery}\n placeholder=\"Search or run a command…\"\n className=\"flex-1 bg-transparent text-base outline-none placeholder:text-muted-foreground\"\n />\n <Kbd className=\"hidden sm:inline-flex\">esc</Kbd>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group heading={group.heading} className={GROUP_HEADING_CLASSES}>\n {group.items.map((item) => (\n <CommandRow\n key={item.id}\n item={item}\n primary={primary}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-muted/20 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2\">\n <div className=\"flex items-center gap-3\">\n {footerSlot}\n {showKeyboardHints && (\n <span className=\"hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex\">\n <span className=\"flex items-center gap-1\"><Kbd>↑</Kbd><Kbd>↓</Kbd> Navigate</span>\n <span className=\"flex items-center gap-1\"><Kbd>↵</Kbd> Open</span>\n </span>\n )}\n </div>\n {brandSlot ?? (\n <span className=\"flex shrink-0 items-center gap-2 text-2xs text-muted-foreground/50\">\n medialane\n <Kbd className=\"hidden sm:inline-flex\">⌘K</Kbd>\n </span>\n )}\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -20,7 +20,7 @@ function Kbd({ children, className }) {
|
|
|
20
20
|
{
|
|
21
21
|
className: cn(
|
|
22
22
|
"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5",
|
|
23
|
-
"font-sans text-
|
|
23
|
+
"font-sans text-2xs leading-none text-muted-foreground",
|
|
24
24
|
className
|
|
25
25
|
),
|
|
26
26
|
children
|
|
@@ -64,7 +64,7 @@ function CommandRow({ item, primary, onSelect }) {
|
|
|
64
64
|
{
|
|
65
65
|
className: cn(
|
|
66
66
|
"block truncate leading-tight",
|
|
67
|
-
primary ? "text-
|
|
67
|
+
primary ? "text-base font-medium" : "text-sm"
|
|
68
68
|
),
|
|
69
69
|
children: item.label
|
|
70
70
|
}
|
|
@@ -148,7 +148,7 @@ function NavCommandMenu({
|
|
|
148
148
|
/* @__PURE__ */ jsx(
|
|
149
149
|
motion.div,
|
|
150
150
|
{
|
|
151
|
-
className: "fixed inset-0 z-[101] flex items-end justify-center p-3 pb-
|
|
151
|
+
className: "fixed inset-0 z-[101] flex items-end justify-center p-3 pb-[calc(1rem+env(safe-area-inset-bottom))] sm:items-center sm:p-4",
|
|
152
152
|
initial: { opacity: 0, y: 24, scale: 1 },
|
|
153
153
|
animate: { opacity: 1, y: 0, scale: 1 },
|
|
154
154
|
exit: { opacity: 0, y: 24 },
|
|
@@ -181,7 +181,7 @@ function NavCommandMenu({
|
|
|
181
181
|
value: query,
|
|
182
182
|
onValueChange: setQuery,
|
|
183
183
|
placeholder: "Search or run a command\u2026",
|
|
184
|
-
className: "flex-1 bg-transparent text-
|
|
184
|
+
className: "flex-1 bg-transparent text-base outline-none placeholder:text-muted-foreground"
|
|
185
185
|
}
|
|
186
186
|
),
|
|
187
187
|
/* @__PURE__ */ jsx(Kbd, { className: "hidden sm:inline-flex", children: "esc" }),
|
|
@@ -229,7 +229,7 @@ function NavCommandMenu({
|
|
|
229
229
|
] })
|
|
230
230
|
] })
|
|
231
231
|
] }),
|
|
232
|
-
brandSlot ?? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2 text-
|
|
232
|
+
brandSlot ?? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2 text-2xs text-muted-foreground/50", children: [
|
|
233
233
|
"medialane",
|
|
234
234
|
/* @__PURE__ */ jsx(Kbd, { className: "hidden sm:inline-flex", children: "\u2318K" })
|
|
235
235
|
] })
|