@medialane/ui 0.323.0 → 0.325.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/components/account/input-otp.d.cts +2 -2
  2. package/dist/components/account/input-otp.d.ts +2 -2
  3. package/dist/components/button.d.cts +2 -2
  4. package/dist/components/button.d.ts +2 -2
  5. package/dist/components/collection-filters.cjs +0 -1
  6. package/dist/components/collection-filters.cjs.map +1 -1
  7. package/dist/components/collection-filters.js +0 -1
  8. package/dist/components/collection-filters.js.map +1 -1
  9. package/dist/components/currency-icon.cjs +7 -2
  10. package/dist/components/currency-icon.cjs.map +1 -1
  11. package/dist/components/currency-icon.d.cts +2 -1
  12. package/dist/components/currency-icon.d.ts +2 -1
  13. package/dist/components/currency-icon.js +5 -1
  14. package/dist/components/currency-icon.js.map +1 -1
  15. package/dist/components/ip-type-page.cjs +1 -1
  16. package/dist/components/ip-type-page.cjs.map +1 -1
  17. package/dist/components/ip-type-page.js +1 -1
  18. package/dist/components/ip-type-page.js.map +1 -1
  19. package/dist/components/launchpad/edition-mint-schema.d.cts +4 -4
  20. package/dist/components/launchpad/edition-mint-schema.d.ts +4 -4
  21. package/dist/components/listing-card.cjs +17 -12
  22. package/dist/components/listing-card.cjs.map +1 -1
  23. package/dist/components/listing-card.js +17 -12
  24. package/dist/components/listing-card.js.map +1 -1
  25. package/dist/components/marketplace-filters.cjs +1 -1
  26. package/dist/components/marketplace-filters.cjs.map +1 -1
  27. package/dist/components/marketplace-filters.js +1 -1
  28. package/dist/components/marketplace-filters.js.map +1 -1
  29. package/dist/utils/media-wallet/use-erc20-balance.d.cts +1 -1
  30. package/dist/utils/media-wallet/use-erc20-balance.d.ts +1 -1
  31. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/ip-type-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport { SlidersHorizontal, Search, X as XIcon } from \"lucide-react\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { Badge } from \"./badge.js\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.js\";\nimport { Input } from \"./input.js\";\nimport { PageContainer } from \"./page-container.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { IP_TYPE_CONFIG, IP_TYPE_MAP } from \"./ip-type-badge.js\";\nimport { TokenCard, TokenCardSkeleton } from \"./token-card.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { useTokensByIpType } from \"../utils/use-tokens-by-ip-type.js\";\nimport { emptyMessages } from \"../utils/empty-messages.js\";\n\nconst PAGE_SIZE = 24;\n\nfunction FilterChip({ active, onClick, label }: { active: boolean; onClick: () => void; label: string }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n active\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n {label}\n </button>\n );\n}\n\nexport interface IpTypePageProps {\n slug: string;\n useMarketActions: () => CollectionMarketActions;\n}\n\nexport function IpTypePage({ slug, useMarketActions }: IpTypePageProps) {\n const { walletAddress } = useMedialaneApp();\n const market = useMarketActions();\n const config = IP_TYPE_MAP[slug];\n const Icon = config?.icon;\n const [page, setPage] = useState(1);\n const [listedOnly, setListedOnly] = useState(false);\n const [allTokens, setAllTokens] = useState<ApiToken[]>([]);\n const prevSlug = useRef(slug);\n\n const [filtersOpen, setFiltersOpen] = useState(false);\n const [sortOrder, setSortOrder] = useState<\"recent\" | \"price_asc\" | \"price_desc\">(\"recent\");\n const [licenseFilter, setLicenseFilter] = useState<string>(\"all\");\n const [creatorSearchInput, setCreatorSearchInput] = useState(\"\");\n const [creatorSearch, setCreatorSearch] = useState(\"\");\n\n useEffect(() => {\n if (prevSlug.current !== slug) {\n prevSlug.current = slug;\n setPage(1);\n setAllTokens([]);\n }\n }, [slug]);\n\n useEffect(() => {\n const t = setTimeout(() => setCreatorSearch(creatorSearchInput), 300);\n return () => clearTimeout(t);\n }, [creatorSearchInput]);\n\n const { tokens, meta, isLoading } = useTokensByIpType(slug, page, PAGE_SIZE);\n\n useEffect(() => {\n if (isLoading) return;\n if (page === 1) {\n setAllTokens(tokens);\n } else {\n setAllTokens((prev) => {\n const seen = new Set(prev.map((t) => `${t.contractAddress}:${t.tokenId}`));\n const fresh = tokens.filter((t) => !seen.has(`${t.contractAddress}:${t.tokenId}`));\n return fresh.length > 0 ? [...prev, ...fresh] : prev;\n });\n }\n }, [tokens, isLoading, page]);\n\n const activeFilterCount =\n (sortOrder !== \"recent\" ? 1 : 0) +\n (licenseFilter !== \"all\" ? 1 : 0) +\n (creatorSearch !== \"\" ? 1 : 0);\n const activeChipCount = activeFilterCount + (listedOnly ? 1 : 0);\n const hasActiveFilters = activeChipCount > 0;\n const resetAll = () => {\n setSortOrder(\"recent\");\n setLicenseFilter(\"all\");\n setCreatorSearch(\"\");\n setCreatorSearchInput(\"\");\n setListedOnly(false);\n };\n\n let displayed = listedOnly\n ? allTokens.filter((t) => (t.activeOrders?.length ?? 0) > 0)\n : [...allTokens];\n\n if (licenseFilter !== \"all\") {\n displayed = displayed.filter((t) =>\n t.metadata?.attributes?.some(\n (a) => a.trait_type === \"License\" && a.value === licenseFilter\n )\n );\n }\n if (creatorSearch) {\n const q = creatorSearch.toLowerCase();\n displayed = displayed.filter((t) => {\n const creatorVal =\n t.metadata?.attributes?.find((a) => a.trait_type === \"Creator\")?.value ?? \"\";\n return (\n String(creatorVal).toLowerCase().includes(q) ||\n t.contractAddress.toLowerCase().includes(q)\n );\n });\n }\n if (sortOrder === \"price_asc\") {\n displayed = [...displayed].sort((a, b) => {\n const pa = parseFloat(a.activeOrders?.[0]?.price?.formatted ?? \"Infinity\");\n const pb = parseFloat(b.activeOrders?.[0]?.price?.formatted ?? \"Infinity\");\n return pa - pb;\n });\n } else if (sortOrder === \"price_desc\") {\n displayed = [...displayed].sort((a, b) => {\n const pa = parseFloat(a.activeOrders?.[0]?.price?.formatted ?? \"0\");\n const pb = parseFloat(b.activeOrders?.[0]?.price?.formatted ?? \"0\");\n return pb - pa;\n });\n }\n\n const isInitialLoading = isLoading && allTokens.length === 0;\n const isLoadingMore = isLoading && allTokens.length > 0;\n const hasMore = meta?.total != null ? allTokens.length < meta.total : false;\n const listedCount = allTokens.filter((t) => (t.activeOrders?.length ?? 0) > 0).length;\n\n return (\n <PageContainer className=\"box-border max-w-full pt-20 pb-16 space-y-8\">\n\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-4\">\n {config && Icon && (\n <div className={cn(\"h-14 w-14 rounded-2xl flex items-center justify-center shadow-lg\", config.bgClass)}>\n <Icon className={cn(\"h-7 w-7\", config.colorClass)} />\n </div>\n )}\n <div>\n <h1 className=\"text-3xl font-semibold\">{config?.label ?? slug}</h1>\n <p className=\"text-muted-foreground mt-0.5\">\n {meta?.total != null ? (\n <>{meta.total.toLocaleString()} assets · {listedCount} market</>\n ) : (\n \"Browsing indexed digital assets on Medialane\"\n )}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => setFiltersOpen(true)}\n className={cn(\n \"inline-flex items-center gap-1.5 text-xs px-3.5 py-2 rounded-full border font-medium transition-colors\",\n hasActiveFilters\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground\"\n )}\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {activeChipCount > 0 && (\n <span className=\"ml-0.5 bg-primary text-primary-foreground rounded-full px-1.5 text-2xs font-bold leading-4\">\n {activeChipCount}\n </span>\n )}\n </button>\n\n {listedOnly && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => setListedOnly(false)}>\n Listed only\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n {sortOrder !== \"recent\" && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => setSortOrder(\"recent\")}>\n {sortOrder === \"price_asc\" ? \"Price ↑\" : \"Price ↓\"}\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n {licenseFilter !== \"all\" && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => setLicenseFilter(\"all\")}>\n {licenseFilter}\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n {creatorSearch && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => { setCreatorSearch(\"\"); setCreatorSearchInput(\"\"); }}>\n {creatorSearch.length > 10 ? `${creatorSearch.slice(0, 10)}…` : creatorSearch}\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n </div>\n\n <Dialog open={filtersOpen} onOpenChange={setFiltersOpen}>\n <DialogContent className=\"w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]\">\n <div className=\"flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12\">\n <DialogTitle className=\"text-base font-bold flex items-center gap-2\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </DialogTitle>\n {hasActiveFilters && (\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 text-xs text-muted-foreground\" onClick={resetAll}>\n Clear all\n </Button>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4 space-y-5\">\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Status</p>\n <div className=\"flex flex-wrap gap-1.5\">\n <FilterChip active={!listedOnly} onClick={() => setListedOnly(false)} label=\"All\" />\n <FilterChip\n active={listedOnly}\n onClick={() => setListedOnly(true)}\n label={`Listed only${listedCount > 0 ? ` (${listedCount})` : \"\"}`}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Sort</p>\n <div className=\"flex flex-wrap gap-1.5\">\n <FilterChip active={sortOrder === \"recent\"} onClick={() => setSortOrder(\"recent\")} label=\"Recent\" />\n <FilterChip active={sortOrder === \"price_asc\"} onClick={() => setSortOrder(\"price_asc\")} label=\"Price ↑\" />\n <FilterChip active={sortOrder === \"price_desc\"} onClick={() => setSortOrder(\"price_desc\")} label=\"Price ↓\" />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">License</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {[\"all\", \"CC0\", \"CC BY\", \"CC BY-SA\", \"CC BY-NC\", \"CC BY-ND\", \"Custom\"].map((lic) => (\n <FilterChip\n key={lic}\n active={licenseFilter === lic}\n onClick={() => setLicenseFilter(lic)}\n label={lic === \"all\" ? \"All licenses\" : lic}\n />\n ))}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Creator</p>\n <div className=\"relative\">\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground\" />\n <Input\n className=\"h-9 text-sm pl-8\"\n placeholder=\"Name or wallet address…\"\n value={creatorSearchInput}\n onChange={(e) => setCreatorSearchInput(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"space-y-2 pt-1 border-t border-border/60\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">IP Type</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {IP_TYPE_CONFIG.map((t) => (\n <Link\n key={t.slug}\n href={`/${t.slug}`}\n className={cn(\n \"inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n t.slug === slug\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n <t.icon className={cn(\"h-3 w-3\", t.slug === slug ? t.colorClass : \"\")} />\n {t.label}\n </Link>\n ))}\n </div>\n </div>\n </div>\n\n <div className=\"px-5 py-3 border-t border-border/60\">\n <Button className=\"w-full\" onClick={() => setFiltersOpen(false)}>\n Apply filters\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n\n {isInitialLoading ? (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4\">\n {Array.from({ length: PAGE_SIZE }).map((_, i) => (\n <TokenCardSkeleton key={i} />\n ))}\n </div>\n ) : displayed.length === 0 ? (\n <div className=\"py-24 flex flex-col items-center gap-4 text-center\">\n {config && Icon && (\n <div className={cn(\"h-16 w-16 rounded-2xl flex items-center justify-center\", config.bgClass)}>\n <Icon className={cn(\"h-8 w-8\", config.colorClass)} />\n </div>\n )}\n <div>\n <p className=\"font-semibold text-lg\">\n {listedOnly ? \"Listed assets appear here\" : emptyMessages.assetsOfType.title}\n </p>\n <p className=\"text-muted-foreground text-sm mt-1\">\n {listedOnly\n ? \"Try removing the 'Listed only' filter to see all assets.\"\n : emptyMessages.assetsOfType.description}\n </p>\n </div>\n <Button asChild variant=\"outline\">\n <Link href=\"/launchpad/single-editions\">Create Asset</Link>\n </Button>\n </div>\n ) : (\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4\">\n {displayed.map((token) => {\n const listing = token.activeOrders?.find((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\");\n const isOwner = !!walletAddress && !!token.owner && isSameAddress(token.owner, walletAddress);\n return (\n <TokenCard\n key={`${token.contractAddress}:${token.tokenId}`}\n token={token}\n isOwner={isOwner}\n onBuy={!isOwner && listing ? () => market.buy(listing) : undefined}\n onOffer={isOwner ? undefined : market.offer}\n />\n );\n })}\n </div>\n\n {!listedOnly && (\n <LoadMoreSentinel\n hasMore={hasMore}\n isLoading={isLoadingMore}\n onLoadMore={() => setPage((p) => p + 1)}\n />\n )}\n </div>\n )}\n\n {market.dialogs}\n </PageContainer>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/ip-type-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport { SlidersHorizontal, Search, X as XIcon } from \"lucide-react\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { Badge } from \"./badge.js\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.js\";\nimport { Input } from \"./input.js\";\nimport { PageContainer } from \"./page-container.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { IP_TYPE_CONFIG, IP_TYPE_MAP } from \"./ip-type-badge.js\";\nimport { TokenCard, TokenCardSkeleton } from \"./token-card.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport type { CollectionMarketActions } from \"./collection-page.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { useTokensByIpType } from \"../utils/use-tokens-by-ip-type.js\";\nimport { emptyMessages } from \"../utils/empty-messages.js\";\n\nconst PAGE_SIZE = 24;\n\nfunction FilterChip({ active, onClick, label }: { active: boolean; onClick: () => void; label: string }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n active\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n {label}\n </button>\n );\n}\n\nexport interface IpTypePageProps {\n slug: string;\n useMarketActions: () => CollectionMarketActions;\n}\n\nexport function IpTypePage({ slug, useMarketActions }: IpTypePageProps) {\n const { walletAddress } = useMedialaneApp();\n const market = useMarketActions();\n const config = IP_TYPE_MAP[slug];\n const Icon = config?.icon;\n const [page, setPage] = useState(1);\n const [listedOnly, setListedOnly] = useState(false);\n const [allTokens, setAllTokens] = useState<ApiToken[]>([]);\n const prevSlug = useRef(slug);\n\n const [filtersOpen, setFiltersOpen] = useState(false);\n const [sortOrder, setSortOrder] = useState<\"recent\" | \"price_asc\" | \"price_desc\">(\"recent\");\n const [licenseFilter, setLicenseFilter] = useState<string>(\"all\");\n const [creatorSearchInput, setCreatorSearchInput] = useState(\"\");\n const [creatorSearch, setCreatorSearch] = useState(\"\");\n\n useEffect(() => {\n if (prevSlug.current !== slug) {\n prevSlug.current = slug;\n setPage(1);\n setAllTokens([]);\n }\n }, [slug]);\n\n useEffect(() => {\n const t = setTimeout(() => setCreatorSearch(creatorSearchInput), 300);\n return () => clearTimeout(t);\n }, [creatorSearchInput]);\n\n const { tokens, meta, isLoading } = useTokensByIpType(slug, page, PAGE_SIZE);\n\n useEffect(() => {\n if (isLoading) return;\n if (page === 1) {\n setAllTokens(tokens);\n } else {\n setAllTokens((prev) => {\n const seen = new Set(prev.map((t) => `${t.contractAddress}:${t.tokenId}`));\n const fresh = tokens.filter((t) => !seen.has(`${t.contractAddress}:${t.tokenId}`));\n return fresh.length > 0 ? [...prev, ...fresh] : prev;\n });\n }\n }, [tokens, isLoading, page]);\n\n const activeFilterCount =\n (sortOrder !== \"recent\" ? 1 : 0) +\n (licenseFilter !== \"all\" ? 1 : 0) +\n (creatorSearch !== \"\" ? 1 : 0);\n const activeChipCount = activeFilterCount + (listedOnly ? 1 : 0);\n const hasActiveFilters = activeChipCount > 0;\n const resetAll = () => {\n setSortOrder(\"recent\");\n setLicenseFilter(\"all\");\n setCreatorSearch(\"\");\n setCreatorSearchInput(\"\");\n setListedOnly(false);\n };\n\n let displayed = listedOnly\n ? allTokens.filter((t) => (t.activeOrders?.length ?? 0) > 0)\n : [...allTokens];\n\n if (licenseFilter !== \"all\") {\n displayed = displayed.filter((t) =>\n t.metadata?.attributes?.some(\n (a) => a.trait_type === \"License\" && a.value === licenseFilter\n )\n );\n }\n if (creatorSearch) {\n const q = creatorSearch.toLowerCase();\n displayed = displayed.filter((t) => {\n const creatorVal =\n t.metadata?.attributes?.find((a) => a.trait_type === \"Creator\")?.value ?? \"\";\n return (\n String(creatorVal).toLowerCase().includes(q) ||\n t.contractAddress.toLowerCase().includes(q)\n );\n });\n }\n if (sortOrder === \"price_asc\") {\n displayed = [...displayed].sort((a, b) => {\n const pa = parseFloat(a.activeOrders?.[0]?.price?.formatted ?? \"Infinity\");\n const pb = parseFloat(b.activeOrders?.[0]?.price?.formatted ?? \"Infinity\");\n return pa - pb;\n });\n } else if (sortOrder === \"price_desc\") {\n displayed = [...displayed].sort((a, b) => {\n const pa = parseFloat(a.activeOrders?.[0]?.price?.formatted ?? \"0\");\n const pb = parseFloat(b.activeOrders?.[0]?.price?.formatted ?? \"0\");\n return pb - pa;\n });\n }\n\n const isInitialLoading = isLoading && allTokens.length === 0;\n const isLoadingMore = isLoading && allTokens.length > 0;\n const hasMore = meta?.total != null ? allTokens.length < meta.total : false;\n const listedCount = allTokens.filter((t) => (t.activeOrders?.length ?? 0) > 0).length;\n\n return (\n <PageContainer className=\"box-border max-w-full pt-20 pb-16 space-y-8\">\n\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-4\">\n {config && Icon && (\n <div className={cn(\"h-14 w-14 rounded-2xl flex items-center justify-center shadow-lg\", config.bgClass)}>\n <Icon className={cn(\"h-7 w-7\", config.colorClass)} />\n </div>\n )}\n <div>\n <h1 className=\"text-3xl font-semibold\">{config?.label ?? slug}</h1>\n <p className=\"text-muted-foreground mt-0.5\">\n {meta?.total != null ? (\n <>{meta.total.toLocaleString()} assets · {listedCount} market</>\n ) : (\n \"Browsing indexed digital assets on Medialane\"\n )}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => setFiltersOpen(true)}\n aria-label=\"Filters\"\n className={cn(\n \"inline-flex items-center gap-1.5 text-xs px-3.5 py-2 rounded-full border font-medium transition-colors\",\n hasActiveFilters\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground\"\n )}\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n {activeChipCount > 0 && (\n <span className=\"ml-0.5 bg-primary text-primary-foreground rounded-full px-1.5 text-2xs font-bold leading-4\">\n {activeChipCount}\n </span>\n )}\n </button>\n\n {listedOnly && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => setListedOnly(false)}>\n Listed only\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n {sortOrder !== \"recent\" && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => setSortOrder(\"recent\")}>\n {sortOrder === \"price_asc\" ? \"Price ↑\" : \"Price ↓\"}\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n {licenseFilter !== \"all\" && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => setLicenseFilter(\"all\")}>\n {licenseFilter}\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n {creatorSearch && (\n <Badge variant=\"secondary\" className=\"cursor-pointer gap-1 text-xs\" onClick={() => { setCreatorSearch(\"\"); setCreatorSearchInput(\"\"); }}>\n {creatorSearch.length > 10 ? `${creatorSearch.slice(0, 10)}…` : creatorSearch}\n <XIcon className=\"h-3 w-3\" />\n </Badge>\n )}\n </div>\n\n <Dialog open={filtersOpen} onOpenChange={setFiltersOpen}>\n <DialogContent className=\"w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]\">\n <div className=\"flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12\">\n <DialogTitle className=\"text-base font-bold flex items-center gap-2\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </DialogTitle>\n {hasActiveFilters && (\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 text-xs text-muted-foreground\" onClick={resetAll}>\n Clear all\n </Button>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4 space-y-5\">\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Status</p>\n <div className=\"flex flex-wrap gap-1.5\">\n <FilterChip active={!listedOnly} onClick={() => setListedOnly(false)} label=\"All\" />\n <FilterChip\n active={listedOnly}\n onClick={() => setListedOnly(true)}\n label={`Listed only${listedCount > 0 ? ` (${listedCount})` : \"\"}`}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Sort</p>\n <div className=\"flex flex-wrap gap-1.5\">\n <FilterChip active={sortOrder === \"recent\"} onClick={() => setSortOrder(\"recent\")} label=\"Recent\" />\n <FilterChip active={sortOrder === \"price_asc\"} onClick={() => setSortOrder(\"price_asc\")} label=\"Price ↑\" />\n <FilterChip active={sortOrder === \"price_desc\"} onClick={() => setSortOrder(\"price_desc\")} label=\"Price ↓\" />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">License</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {[\"all\", \"CC0\", \"CC BY\", \"CC BY-SA\", \"CC BY-NC\", \"CC BY-ND\", \"Custom\"].map((lic) => (\n <FilterChip\n key={lic}\n active={licenseFilter === lic}\n onClick={() => setLicenseFilter(lic)}\n label={lic === \"all\" ? \"All licenses\" : lic}\n />\n ))}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Creator</p>\n <div className=\"relative\">\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground\" />\n <Input\n className=\"h-9 text-sm pl-8\"\n placeholder=\"Name or wallet address…\"\n value={creatorSearchInput}\n onChange={(e) => setCreatorSearchInput(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"space-y-2 pt-1 border-t border-border/60\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">IP Type</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {IP_TYPE_CONFIG.map((t) => (\n <Link\n key={t.slug}\n href={`/${t.slug}`}\n className={cn(\n \"inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n t.slug === slug\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n )}\n >\n <t.icon className={cn(\"h-3 w-3\", t.slug === slug ? t.colorClass : \"\")} />\n {t.label}\n </Link>\n ))}\n </div>\n </div>\n </div>\n\n <div className=\"px-5 py-3 border-t border-border/60\">\n <Button className=\"w-full\" onClick={() => setFiltersOpen(false)}>\n Apply filters\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n\n {isInitialLoading ? (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4\">\n {Array.from({ length: PAGE_SIZE }).map((_, i) => (\n <TokenCardSkeleton key={i} />\n ))}\n </div>\n ) : displayed.length === 0 ? (\n <div className=\"py-24 flex flex-col items-center gap-4 text-center\">\n {config && Icon && (\n <div className={cn(\"h-16 w-16 rounded-2xl flex items-center justify-center\", config.bgClass)}>\n <Icon className={cn(\"h-8 w-8\", config.colorClass)} />\n </div>\n )}\n <div>\n <p className=\"font-semibold text-lg\">\n {listedOnly ? \"Listed assets appear here\" : emptyMessages.assetsOfType.title}\n </p>\n <p className=\"text-muted-foreground text-sm mt-1\">\n {listedOnly\n ? \"Try removing the 'Listed only' filter to see all assets.\"\n : emptyMessages.assetsOfType.description}\n </p>\n </div>\n <Button asChild variant=\"outline\">\n <Link href=\"/launchpad/single-editions\">Create Asset</Link>\n </Button>\n </div>\n ) : (\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4\">\n {displayed.map((token) => {\n const listing = token.activeOrders?.find((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\");\n const isOwner = !!walletAddress && !!token.owner && isSameAddress(token.owner, walletAddress);\n return (\n <TokenCard\n key={`${token.contractAddress}:${token.tokenId}`}\n token={token}\n isOwner={isOwner}\n onBuy={!isOwner && listing ? () => market.buy(listing) : undefined}\n onOffer={isOwner ? undefined : market.offer}\n />\n );\n })}\n </div>\n\n {!listedOnly && (\n <LoadMoreSentinel\n hasMore={hasMore}\n isLoading={isLoadingMore}\n onLoadMore={() => setPage((p) => p + 1)}\n />\n )}\n </div>\n )}\n\n {market.dialogs}\n </PageContainer>\n );\n}\n"],"mappings":"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","names":[]}
@@ -19,13 +19,13 @@ declare const editionMintSchema: z.ZodObject<{
19
19
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
20
20
  licenseType: string;
21
21
  commercialUse: "Yes" | "No";
22
+ value: string;
23
+ recipient: string;
22
24
  derivatives: "Allowed" | "Not Allowed" | "Share-Alike";
23
25
  attribution: "Required" | "Not Required";
24
26
  geographicScope: string;
25
27
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
26
28
  royalty: number;
27
- value: string;
28
- recipient: string;
29
29
  description?: string | undefined;
30
30
  external_url?: string | undefined;
31
31
  }, {
@@ -33,13 +33,13 @@ declare const editionMintSchema: z.ZodObject<{
33
33
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
34
34
  licenseType: string;
35
35
  commercialUse: "Yes" | "No";
36
+ value: string;
37
+ recipient: string;
36
38
  derivatives: "Allowed" | "Not Allowed" | "Share-Alike";
37
39
  attribution: "Required" | "Not Required";
38
40
  geographicScope: string;
39
41
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
40
42
  royalty: number;
41
- value: string;
42
- recipient: string;
43
43
  description?: string | undefined;
44
44
  external_url?: string | undefined;
45
45
  }>;
@@ -19,13 +19,13 @@ declare const editionMintSchema: z.ZodObject<{
19
19
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
20
20
  licenseType: string;
21
21
  commercialUse: "Yes" | "No";
22
+ value: string;
23
+ recipient: string;
22
24
  derivatives: "Allowed" | "Not Allowed" | "Share-Alike";
23
25
  attribution: "Required" | "Not Required";
24
26
  geographicScope: string;
25
27
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
26
28
  royalty: number;
27
- value: string;
28
- recipient: string;
29
29
  description?: string | undefined;
30
30
  external_url?: string | undefined;
31
31
  }, {
@@ -33,13 +33,13 @@ declare const editionMintSchema: z.ZodObject<{
33
33
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
34
34
  licenseType: string;
35
35
  commercialUse: "Yes" | "No";
36
+ value: string;
37
+ recipient: string;
36
38
  derivatives: "Allowed" | "Not Allowed" | "Share-Alike";
37
39
  attribution: "Required" | "Not Required";
38
40
  geographicScope: string;
39
41
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
40
42
  royalty: number;
41
- value: string;
42
- recipient: string;
43
43
  description?: string | undefined;
44
44
  external_url?: string | undefined;
45
45
  }>;
@@ -39,6 +39,7 @@ var import_link = __toESM(require("./link.js"), 1);
39
39
  var import_lucide_react = require("lucide-react");
40
40
  var import_motion_primitives = require("./motion-primitives.js");
41
41
  var import_animated_token_media = require("./animated-token-media.js");
42
+ var import_currency_icon = require("./currency-icon.js");
42
43
  var import_cn = require("../utils/cn.js");
43
44
  var import_format = require("../utils/format.js");
44
45
  var import_time = require("../utils/time.js");
@@ -53,10 +54,14 @@ function CardPrice({
53
54
  if (!amountFormatted) return null;
54
55
  const amount = (0, import_format.formatDisplayPrice)(amountFormatted);
55
56
  const primary = usdValue ?? amount;
56
- const secondary = !usdValue ? currency : (0, import_format.isStableCurrency)(currency) ? currency : `${amount} ${currency}`;
57
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "flex items-baseline gap-1.5 tabular-nums", children: [
57
+ const showAmount = !!usdValue && !(0, import_format.isStableCurrency)(currency);
58
+ const marker = (0, import_currency_icon.hasCurrencyIcon)(currency) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_currency_icon.CurrencyIcon, { symbol: currency, size: 14 }) : currency;
59
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "flex items-center gap-1.5 tabular-nums", children: [
58
60
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-base font-bold tracking-tight", children: primary }),
59
- secondary && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground", children: secondary })
61
+ marker || showAmount ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [
62
+ marker,
63
+ showAmount ? amount : null
64
+ ] }) : null
60
65
  ] });
61
66
  }
62
67
  function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }) {
@@ -71,8 +76,8 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
71
76
  const assetHref = (0, import_sdk.assetHref)("STARKNET", order.nftContract ?? "", order.nftTokenId ?? "");
72
77
  const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);
73
78
  if (compact) {
74
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_motion_primitives.MotionCard, { className: "group", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_link.default, { href: assetHref, className: "block", children: [
75
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative aspect-square bg-muted overflow-hidden rounded-xl", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
79
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_motion_primitives.MotionCard, { className: "group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_link.default, { href: assetHref, className: "block", children: [
80
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative aspect-square bg-muted overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
76
81
  import_animated_token_media.AnimatedTokenMedia,
77
82
  {
78
83
  image,
@@ -87,15 +92,15 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
87
92
  fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15" })
88
93
  }
89
94
  ) }),
90
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-0.5 pt-2.5 space-y-0.5", children: [
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-2.5 space-y-0.5", children: [
91
96
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-medium truncate", children: name }),
92
97
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardPrice, { amountFormatted: order.price?.formatted, currency: order.price?.currency, usdValue }),
93
98
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs text-muted-foreground", children: (0, import_time.timeAgo)(order.createdAt) })
94
99
  ] })
95
100
  ] }) });
96
101
  }
97
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_motion_primitives.MotionCard, { className: "group", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_link.default, { href: assetHref, className: "block", children: [
98
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative aspect-square bg-muted overflow-hidden rounded-xl", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
102
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_motion_primitives.MotionCard, { className: "group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_link.default, { href: assetHref, className: "block", children: [
103
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative aspect-square bg-muted overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
99
104
  import_animated_token_media.AnimatedTokenMedia,
100
105
  {
101
106
  image,
@@ -110,7 +115,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
110
115
  fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15" })
111
116
  }
112
117
  ) }),
113
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-0.5 pt-3 space-y-2.5", children: [
118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-3.5 space-y-2.5", children: [
114
119
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0 space-y-1", children: [
115
120
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-medium truncate", children: name }),
116
121
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardPrice, { amountFormatted: order.price?.formatted, currency: order.price?.currency, usdValue })
@@ -172,9 +177,9 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
172
177
  ] }) });
173
178
  }
174
179
  function ListingCardSkeleton() {
175
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
176
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "aspect-square w-full rounded-xl bg-muted animate-pulse" }),
177
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-0.5 pt-3 space-y-2.5", children: [
180
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card", children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "aspect-square w-full bg-muted animate-pulse" }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-3.5 space-y-2.5", children: [
178
183
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1", children: [
179
184
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-4 w-3/4 bg-muted animate-pulse rounded" }),
180
185
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-5 w-1/2 bg-muted animate-pulse rounded" })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, type ApiOrder, type Chain } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nfunction CardPrice({\n amountFormatted, currency, usdValue,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n}) {\n if (!amountFormatted) return null;\n const amount = formatDisplayPrice(amountFormatted);\n const primary = usdValue ?? amount;\n const secondary = !usdValue ? currency : isStableCurrency(currency) ? currency : `${amount} ${currency}`;\n return (\n <p className=\"flex items-baseline gap-1.5 tabular-nums\">\n <span className=\"text-base font-bold tracking-tight\">{primary}</span>\n {secondary && <span className=\"text-xs text-muted-foreground\">{secondary}</span>}\n </p>\n );\n}\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n\n overflowMenu?: React.ReactNode;\n\n primaryAction?: React.ReactNode;\n compact?: boolean;\n\n imageUrl?: string | null;\n\n usdValue?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }: ListingCardProps) {\n const resolveImage = useImageUrl();\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 ? resolveImage(order.token.image, { width: 480 }) : null);\n\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 = buildAssetHref(\"STARKNET\", order.nftContract ?? \"\", order.nftTokenId ?? \"\");\n\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n if (compact) {\n return (\n <MotionCard className=\"group\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden rounded-xl\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n <div className=\"px-0.5 pt-2.5 space-y-0.5\">\n <p className=\"text-xs font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n return (\n <MotionCard className=\"group\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden rounded-xl\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n\n <div className=\"px-0.5 pt-3 space-y-2.5\">\n <div className=\"min-w-0 space-y-1\">\n <p className=\"text-sm font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n </div>\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>\n <div className=\"aspect-square w-full rounded-xl bg-muted animate-pulse\" />\n <div className=\"px-0.5 pt-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-5 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-9 w-full bg-muted animate-pulse rounded-[9px]\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["buildAssetHref","Link"]}
1
+ {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { CurrencyIcon, hasCurrencyIcon } from \"./currency-icon.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, type ApiOrder, type Chain } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nfunction CardPrice({\n amountFormatted, currency, usdValue,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n}) {\n if (!amountFormatted) return null;\n const amount = formatDisplayPrice(amountFormatted);\n const primary = usdValue ?? amount;\n const showAmount = !!usdValue && !isStableCurrency(currency);\n const marker = hasCurrencyIcon(currency) ? <CurrencyIcon symbol={currency} size={14} /> : currency;\n return (\n <p className=\"flex items-center gap-1.5 tabular-nums\">\n <span className=\"text-base font-bold tracking-tight\">{primary}</span>\n {marker || showAmount ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\">\n {marker}\n {showAmount ? amount : null}\n </span>\n ) : null}\n </p>\n );\n}\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n\n overflowMenu?: React.ReactNode;\n\n primaryAction?: React.ReactNode;\n compact?: boolean;\n\n imageUrl?: string | null;\n\n usdValue?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }: ListingCardProps) {\n const resolveImage = useImageUrl();\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 ? resolveImage(order.token.image, { width: 480 }) : null);\n\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 = buildAssetHref(\"STARKNET\", order.nftContract ?? \"\", order.nftTokenId ?? \"\");\n\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n if (compact) {\n return (\n <MotionCard className=\"group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n return (\n <MotionCard className=\"group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-2.5\">\n <div className=\"min-w-0 space-y-1\">\n <p className=\"text-sm font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n </div>\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=\"overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3.5 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-5 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-9 w-full bg-muted animate-pulse rounded-[9px]\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["buildAssetHref","Link"]}
@@ -5,6 +5,7 @@ import Link from "./link.js";
5
5
  import { ShoppingCart, Check, Zap, ArrowUpRight } from "lucide-react";
6
6
  import { MotionCard } from "./motion-primitives.js";
7
7
  import { AnimatedTokenMedia } from "./animated-token-media.js";
8
+ import { CurrencyIcon, hasCurrencyIcon } from "./currency-icon.js";
8
9
  import { cn } from "../utils/cn.js";
9
10
  import { formatDisplayPrice, isStableCurrency } from "../utils/format.js";
10
11
  import { timeAgo } from "../utils/time.js";
@@ -19,10 +20,14 @@ function CardPrice({
19
20
  if (!amountFormatted) return null;
20
21
  const amount = formatDisplayPrice(amountFormatted);
21
22
  const primary = usdValue ?? amount;
22
- const secondary = !usdValue ? currency : isStableCurrency(currency) ? currency : `${amount} ${currency}`;
23
- return /* @__PURE__ */ jsxs("p", { className: "flex items-baseline gap-1.5 tabular-nums", children: [
23
+ const showAmount = !!usdValue && !isStableCurrency(currency);
24
+ const marker = hasCurrencyIcon(currency) ? /* @__PURE__ */ jsx(CurrencyIcon, { symbol: currency, size: 14 }) : currency;
25
+ return /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 tabular-nums", children: [
24
26
  /* @__PURE__ */ jsx("span", { className: "text-base font-bold tracking-tight", children: primary }),
25
- secondary && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: secondary })
27
+ marker || showAmount ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [
28
+ marker,
29
+ showAmount ? amount : null
30
+ ] }) : null
26
31
  ] });
27
32
  }
28
33
  function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }) {
@@ -37,8 +42,8 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
37
42
  const assetHref = buildAssetHref("STARKNET", order.nftContract ?? "", order.nftTokenId ?? "");
38
43
  const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);
39
44
  if (compact) {
40
- return /* @__PURE__ */ jsx(MotionCard, { className: "group", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
41
- /* @__PURE__ */ jsx("div", { className: "relative aspect-square bg-muted overflow-hidden rounded-xl", children: /* @__PURE__ */ jsx(
45
+ return /* @__PURE__ */ jsx(MotionCard, { className: "group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
46
+ /* @__PURE__ */ jsx("div", { className: "relative aspect-square bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
42
47
  AnimatedTokenMedia,
43
48
  {
44
49
  image,
@@ -53,15 +58,15 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
53
58
  fallback: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15" })
54
59
  }
55
60
  ) }),
56
- /* @__PURE__ */ jsxs("div", { className: "px-0.5 pt-2.5 space-y-0.5", children: [
61
+ /* @__PURE__ */ jsxs("div", { className: "p-2.5 space-y-0.5", children: [
57
62
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium truncate", children: name }),
58
63
  /* @__PURE__ */ jsx(CardPrice, { amountFormatted: order.price?.formatted, currency: order.price?.currency, usdValue }),
59
64
  /* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground", children: timeAgo(order.createdAt) })
60
65
  ] })
61
66
  ] }) });
62
67
  }
63
- return /* @__PURE__ */ jsx(MotionCard, { className: "group", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
64
- /* @__PURE__ */ jsx("div", { className: "relative aspect-square bg-muted overflow-hidden rounded-xl", children: /* @__PURE__ */ jsx(
68
+ return /* @__PURE__ */ jsx(MotionCard, { className: "group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
69
+ /* @__PURE__ */ jsx("div", { className: "relative aspect-square bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
65
70
  AnimatedTokenMedia,
66
71
  {
67
72
  image,
@@ -76,7 +81,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
76
81
  fallback: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15" })
77
82
  }
78
83
  ) }),
79
- /* @__PURE__ */ jsxs("div", { className: "px-0.5 pt-3 space-y-2.5", children: [
84
+ /* @__PURE__ */ jsxs("div", { className: "p-3.5 space-y-2.5", children: [
80
85
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
81
86
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: name }),
82
87
  /* @__PURE__ */ jsx(CardPrice, { amountFormatted: order.price?.formatted, currency: order.price?.currency, usdValue })
@@ -138,9 +143,9 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
138
143
  ] }) });
139
144
  }
140
145
  function ListingCardSkeleton() {
141
- return /* @__PURE__ */ jsxs("div", { children: [
142
- /* @__PURE__ */ jsx("div", { className: "aspect-square w-full rounded-xl bg-muted animate-pulse" }),
143
- /* @__PURE__ */ jsxs("div", { className: "px-0.5 pt-3 space-y-2.5", children: [
146
+ return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card", children: [
147
+ /* @__PURE__ */ jsx("div", { className: "aspect-square w-full bg-muted animate-pulse" }),
148
+ /* @__PURE__ */ jsxs("div", { className: "p-3.5 space-y-2.5", children: [
144
149
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
145
150
  /* @__PURE__ */ jsx("div", { className: "h-4 w-3/4 bg-muted animate-pulse rounded" }),
146
151
  /* @__PURE__ */ jsx("div", { className: "h-5 w-1/2 bg-muted animate-pulse rounded" })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, type ApiOrder, type Chain } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nfunction CardPrice({\n amountFormatted, currency, usdValue,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n}) {\n if (!amountFormatted) return null;\n const amount = formatDisplayPrice(amountFormatted);\n const primary = usdValue ?? amount;\n const secondary = !usdValue ? currency : isStableCurrency(currency) ? currency : `${amount} ${currency}`;\n return (\n <p className=\"flex items-baseline gap-1.5 tabular-nums\">\n <span className=\"text-base font-bold tracking-tight\">{primary}</span>\n {secondary && <span className=\"text-xs text-muted-foreground\">{secondary}</span>}\n </p>\n );\n}\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n\n overflowMenu?: React.ReactNode;\n\n primaryAction?: React.ReactNode;\n compact?: boolean;\n\n imageUrl?: string | null;\n\n usdValue?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }: ListingCardProps) {\n const resolveImage = useImageUrl();\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 ? resolveImage(order.token.image, { width: 480 }) : null);\n\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 = buildAssetHref(\"STARKNET\", order.nftContract ?? \"\", order.nftTokenId ?? \"\");\n\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n if (compact) {\n return (\n <MotionCard className=\"group\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden rounded-xl\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n <div className=\"px-0.5 pt-2.5 space-y-0.5\">\n <p className=\"text-xs font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n return (\n <MotionCard className=\"group\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden rounded-xl\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n\n <div className=\"px-0.5 pt-3 space-y-2.5\">\n <div className=\"min-w-0 space-y-1\">\n <p className=\"text-sm font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n </div>\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>\n <div className=\"aspect-square w-full rounded-xl bg-muted animate-pulse\" />\n <div className=\"px-0.5 pt-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-5 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-9 w-full bg-muted animate-pulse rounded-[9px]\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { CurrencyIcon, hasCurrencyIcon } from \"./currency-icon.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, type ApiOrder, type Chain } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nfunction CardPrice({\n amountFormatted, currency, usdValue,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n}) {\n if (!amountFormatted) return null;\n const amount = formatDisplayPrice(amountFormatted);\n const primary = usdValue ?? amount;\n const showAmount = !!usdValue && !isStableCurrency(currency);\n const marker = hasCurrencyIcon(currency) ? <CurrencyIcon symbol={currency} size={14} /> : currency;\n return (\n <p className=\"flex items-center gap-1.5 tabular-nums\">\n <span className=\"text-base font-bold tracking-tight\">{primary}</span>\n {marker || showAmount ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\">\n {marker}\n {showAmount ? amount : null}\n </span>\n ) : null}\n </p>\n );\n}\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n\n overflowMenu?: React.ReactNode;\n\n primaryAction?: React.ReactNode;\n compact?: boolean;\n\n imageUrl?: string | null;\n\n usdValue?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }: ListingCardProps) {\n const resolveImage = useImageUrl();\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 ? resolveImage(order.token.image, { width: 480 }) : null);\n\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 = buildAssetHref(\"STARKNET\", order.nftContract ?? \"\", order.nftTokenId ?? \"\");\n\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n if (compact) {\n return (\n <MotionCard className=\"group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n return (\n <MotionCard className=\"group overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card\">\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\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-2.5\">\n <div className=\"min-w-0 space-y-1\">\n <p className=\"text-sm font-medium truncate\">{name}</p>\n <CardPrice amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n </div>\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=\"overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3.5 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-5 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-9 w-full bg-muted animate-pulse rounded-[9px]\" />\n </div>\n </div>\n );\n}\n"],"mappings":";AA4B6C,cAKrC,YALqC;AA1B7C,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,cAAc,OAAO,KAAK,oBAAoB;AACvD,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC,SAAS,cAAc,uBAAuB;AAC9C,SAAS,UAAU;AACnB,SAAS,oBAAoB,wBAAwB;AACrD,SAAS,eAAe;AACxB,SAAS,gCAAgC;AACzC,SAAS,aAAa,sBAAiD;AACvE,SAAS,mBAAmB;AAI5B,SAAS,UAAU;AAAA,EACjB;AAAA,EAAiB;AAAA,EAAU;AAC7B,GAIG;AACD,MAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAM,SAAS,mBAAmB,eAAe;AACjD,QAAM,UAAU,YAAY;AAC5B,QAAM,aAAa,CAAC,CAAC,YAAY,CAAC,iBAAiB,QAAQ;AAC3D,QAAM,SAAS,gBAAgB,QAAQ,IAAI,oBAAC,gBAAa,QAAQ,UAAU,MAAM,IAAI,IAAK;AAC1F,SACE,qBAAC,OAAE,WAAU,0CACX;AAAA,wBAAC,UAAK,WAAU,sCAAsC,mBAAQ;AAAA,IAC7D,UAAU,aACT,qBAAC,UAAK,WAAU,gEACb;AAAA;AAAA,MACA,aAAa,SAAS;AAAA,OACzB,IACE;AAAA,KACN;AAEJ;AAkBO,SAAS,YAAY,EAAE,OAAO,UAAU,SAAS,OAAO,OAAO,QAAQ,cAAc,eAAe,UAAU,OAAO,UAAU,SAAS,GAAqB;AAClK,QAAM,eAAe,YAAY;AACjC,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,aAAa,MAAM,MAAM,OAAO,EAAE,OAAO,IAAI,CAAC,IAAI;AAE1H,QAAM,eAAe,MAAM,OAAO,gBAAgB;AAClD,QAAM,OAAO,CAAC,EAAE,MAAM,SAAS,MAAM,gBAAgB,yBAAyB,MAAM,MAAM,YAAY,GAAY,MAAM,WAAY;AACpI,QAAM,YAAY,eAAe,YAAY,MAAM,eAAe,IAAI,MAAM,cAAc,EAAE;AAE5F,QAAM,gBAAgB,CAAC,EAAE,SAAS,UAAU,gBAAgB;AAE5D,MAAI,SAAS;AACX,WACE,oBAAC,cAAW,WAAU,oEACpB,+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,4EAA2E;AAAA;AAAA,MAE9F,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,qBACb;AAAA,4BAAC,OAAE,WAAU,gCAAgC,gBAAK;AAAA,QAClD,oBAAC,aAAU,iBAAiB,MAAM,OAAO,WAAW,UAAU,MAAM,OAAO,UAAU,UAAoB;AAAA,QACzG,oBAAC,OAAE,WAAU,kCAAkC,kBAAQ,MAAM,SAAS,GAAE;AAAA,SAC1E;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,cAAW,WAAU,oEACpB,+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,4EAA2E;AAAA;AAAA,IAE9F,GACF;AAAA,IAEA,qBAAC,SAAI,WAAU,qBACb;AAAA,2BAAC,SAAI,WAAU,qBACb;AAAA,4BAAC,OAAE,WAAU,gCAAgC,gBAAK;AAAA,QAClD,oBAAC,aAAU,iBAAiB,MAAM,OAAO,WAAW,UAAU,MAAM,OAAO,UAAU,UAAoB;AAAA,SAC3G;AAAA,MAEC,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,8DACb;AAAA,wBAAC,SAAI,WAAU,+CAA8C;AAAA,IAC7D,qBAAC,SAAI,WAAU,qBACb;AAAA,2BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,SAAI,WAAU,4CAA2C;AAAA,QAC1D,oBAAC,SAAI,WAAU,4CAA2C;AAAA,SAC5D;AAAA,MACA,oBAAC,SAAI,WAAU,mDAAkD;AAAA,OACnE;AAAA,KACF;AAEJ;","names":[]}
@@ -158,13 +158,13 @@ function FiltersButton({ filters }) {
158
158
  "button",
159
159
  {
160
160
  onClick: () => filters.setOpen(true),
161
+ "aria-label": "Filters",
161
162
  className: (0, import_cn.cn)(
162
163
  "relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors shrink-0",
163
164
  filters.count > 0 ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
164
165
  ),
165
166
  children: [
166
167
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.SlidersHorizontal, { className: "h-3.5 w-3.5" }),
167
- "Filters",
168
168
  filters.count > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground", children: filters.count })
169
169
  ]
170
170
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/marketplace-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport Link from \"./link.js\";\nimport { usePathname } from \"next/navigation\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { getTokenBySymbol, parseAmount, SUPPORTED_TOKENS } from \"@medialane/sdk\";\nimport { Input } from \"./input.js\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { HelpIcon } from \"./help-icon.js\";\nimport { IP_TYPES } from \"../data/ip.js\";\nimport { useDebouncedValue } from \"../utils/use-debounced-value.js\";\nimport { cn } from \"../utils/cn.js\";\n\nconst SORT_OPTIONS = [\n { label: \"Recent\", value: \"recent\" },\n { label: \"Price ↑\", value: \"price_asc\" },\n { label: \"Price ↓\", value: \"price_desc\" },\n];\n\nconst TYPE_OPTIONS = [\n { label: \"All\", value: \"\" },\n { label: \"Listings\", value: \"listings\" },\n { label: \"Offers\", value: \"offers\" },\n];\n\nconst CURRENCY_OPTIONS = SUPPORTED_TOKENS.map((t) => ({\n label: t.symbol,\n value: t.symbol,\n icon: <CurrencyIcon symbol={t.symbol} size={13} />,\n}));\n\nfunction toRawAmount(value: string, decimals: number): string | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n try {\n return parseAmount(trimmed, decimals);\n } catch {\n return undefined;\n }\n}\n\nexport function useMarketplaceFilters() {\n const [open, setOpen] = useState(false);\n const [sort, setSort] = useState(\"recent\");\n const [currency, setCurrency] = useState(\"\");\n const [orderType, setOrderType] = useState(\"\");\n const [minInput, setMinInput] = useState(\"\");\n const [maxInput, setMaxInput] = useState(\"\");\n const debouncedMin = useDebouncedValue(minInput, 400);\n const debouncedMax = useDebouncedValue(maxInput, 400);\n\n const decimals = getTokenBySymbol(currency)?.decimals ?? 18;\n const minPrice = minInput ? toRawAmount(debouncedMin, decimals) : undefined;\n const maxPrice = maxInput ? toRawAmount(debouncedMax, decimals) : undefined;\n\n const clearPrice = () => {\n setMinInput(\"\");\n setMaxInput(\"\");\n };\n\n const selectCurrency = (symbol: string) => {\n setCurrency(currency === symbol ? \"\" : symbol);\n clearPrice();\n };\n\n const reset = () => {\n setSort(\"recent\");\n setCurrency(\"\");\n setOrderType(\"\");\n clearPrice();\n };\n\n const count = [sort !== \"recent\", currency, orderType, minPrice || maxPrice].filter(Boolean).length;\n\n return {\n open, setOpen,\n sort, setSort,\n currency, selectCurrency,\n orderType, setOrderType,\n minInput, setMinInput,\n maxInput, setMaxInput,\n minPrice, maxPrice,\n clearPrice, reset, count,\n };\n}\n\nexport type MarketplaceFilters = ReturnType<typeof useMarketplaceFilters>;\n\nconst chipClass = (active: boolean) =>\n cn(\n \"inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n active\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n );\n\nfunction ChipGroup({ title, options, value, onSelect }: {\n title: string;\n options: { label: string; value: string; icon?: ReactNode }[];\n value: string;\n onSelect: (value: string) => void;\n}) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">{title}</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {options.map((opt) => (\n <button key={opt.value} onClick={() => onSelect(opt.value)} className={chipClass(value === opt.value)}>\n {opt.icon}\n <span>{opt.label}</span>\n </button>\n ))}\n </div>\n </div>\n );\n}\n\nfunction IpTypeChip({ href, label }: { href: string; label: string }) {\n const pathname = usePathname();\n return (\n <Link\n href={href}\n className={cn(\n \"text-xs px-3 py-1 rounded-full border transition-colors whitespace-nowrap\",\n pathname === href\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {label}\n </Link>\n );\n}\n\nfunction ActiveChip({ children, onClear }: { children: ReactNode; onClear: () => void }) {\n return (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n {children}\n <button onClick={onClear} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n );\n}\n\nexport function FiltersButton({ filters }: { filters: MarketplaceFilters }) {\n return (\n <button\n onClick={() => filters.setOpen(true)}\n className={cn(\n \"relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors shrink-0\",\n filters.count > 0\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filters.count > 0 && (\n <span className=\"flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground\">\n {filters.count}\n </span>\n )}\n </button>\n );\n}\n\nexport function ActiveFilters({ filters }: { filters: MarketplaceFilters }) {\n const { sort, setSort, orderType, setOrderType, currency, selectCurrency, minPrice, maxPrice, minInput, maxInput, clearPrice, count } = filters;\n if (count === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {sort !== \"recent\" && (\n <ActiveChip onClear={() => setSort(\"recent\")}>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</ActiveChip>\n )}\n {orderType && (\n <ActiveChip onClear={() => setOrderType(\"\")}>{TYPE_OPTIONS.find((o) => o.value === orderType)?.label}</ActiveChip>\n )}\n {currency && (\n <ActiveChip onClear={() => selectCurrency(currency)}>\n <CurrencyIcon symbol={currency} size={12} />\n {currency}\n </ActiveChip>\n )}\n {(minPrice || maxPrice) && (\n <ActiveChip onClear={clearPrice}>{minInput || \"0\"} – {maxInput || \"∞\"}</ActiveChip>\n )}\n </div>\n );\n}\n\nexport function FiltersDialog({ filters }: { filters: MarketplaceFilters }) {\n const { open, setOpen, sort, setSort, orderType, setOrderType, currency, selectCurrency, minInput, setMinInput, maxInput, setMaxInput, reset, count } = filters;\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]\">\n\n <div className=\"flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12\">\n <DialogTitle className=\"text-base font-bold flex items-center gap-2\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </DialogTitle>\n {count > 0 && (\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 text-xs text-muted-foreground\" onClick={reset}>\n Clear all\n </Button>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4 space-y-5\">\n <ChipGroup title=\"Sort\" options={SORT_OPTIONS} value={sort} onSelect={setSort} />\n <ChipGroup title=\"Order type\" options={TYPE_OPTIONS} value={orderType} onSelect={setOrderType} />\n <ChipGroup title=\"Currency\" options={CURRENCY_OPTIONS} value={currency} onSelect={selectCurrency} />\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Price range</p>\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder=\"Min\"\n value={minInput}\n onChange={(e) => setMinInput(e.target.value)}\n className=\"h-9 text-sm\"\n type=\"number\"\n min=\"0\"\n />\n <span className=\"text-sm text-muted-foreground shrink-0\">–</span>\n <Input\n placeholder=\"Max\"\n value={maxInput}\n onChange={(e) => setMaxInput(e.target.value)}\n className=\"h-9 text-sm\"\n type=\"number\"\n min=\"0\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2 pt-1 border-t border-border/60\">\n <p className=\"flex items-center gap-1 text-2xs font-semibold text-muted-foreground\">\n IP Type\n <HelpIcon content=\"Filter by intellectual property category — Art, Audio, Video, Software, and more\" side=\"right\" />\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n <IpTypeChip href=\"/marketplace\" label=\"All\" />\n {IP_TYPES.map((type) => (\n <IpTypeChip key={type} href={`/${type.toLowerCase()}`} label={type} />\n ))}\n </div>\n </div>\n </div>\n\n <div className=\"px-5 py-3 border-t border-border/60\">\n <Button className=\"w-full\" onClick={() => setOpen(false)}>\n Apply filters\n </Button>\n </div>\n\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":["Link"]}
1
+ {"version":3,"sources":["../../src/components/marketplace-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport Link from \"./link.js\";\nimport { usePathname } from \"next/navigation\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { getTokenBySymbol, parseAmount, SUPPORTED_TOKENS } from \"@medialane/sdk\";\nimport { Input } from \"./input.js\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { HelpIcon } from \"./help-icon.js\";\nimport { IP_TYPES } from \"../data/ip.js\";\nimport { useDebouncedValue } from \"../utils/use-debounced-value.js\";\nimport { cn } from \"../utils/cn.js\";\n\nconst SORT_OPTIONS = [\n { label: \"Recent\", value: \"recent\" },\n { label: \"Price ↑\", value: \"price_asc\" },\n { label: \"Price ↓\", value: \"price_desc\" },\n];\n\nconst TYPE_OPTIONS = [\n { label: \"All\", value: \"\" },\n { label: \"Listings\", value: \"listings\" },\n { label: \"Offers\", value: \"offers\" },\n];\n\nconst CURRENCY_OPTIONS = SUPPORTED_TOKENS.map((t) => ({\n label: t.symbol,\n value: t.symbol,\n icon: <CurrencyIcon symbol={t.symbol} size={13} />,\n}));\n\nfunction toRawAmount(value: string, decimals: number): string | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n try {\n return parseAmount(trimmed, decimals);\n } catch {\n return undefined;\n }\n}\n\nexport function useMarketplaceFilters() {\n const [open, setOpen] = useState(false);\n const [sort, setSort] = useState(\"recent\");\n const [currency, setCurrency] = useState(\"\");\n const [orderType, setOrderType] = useState(\"\");\n const [minInput, setMinInput] = useState(\"\");\n const [maxInput, setMaxInput] = useState(\"\");\n const debouncedMin = useDebouncedValue(minInput, 400);\n const debouncedMax = useDebouncedValue(maxInput, 400);\n\n const decimals = getTokenBySymbol(currency)?.decimals ?? 18;\n const minPrice = minInput ? toRawAmount(debouncedMin, decimals) : undefined;\n const maxPrice = maxInput ? toRawAmount(debouncedMax, decimals) : undefined;\n\n const clearPrice = () => {\n setMinInput(\"\");\n setMaxInput(\"\");\n };\n\n const selectCurrency = (symbol: string) => {\n setCurrency(currency === symbol ? \"\" : symbol);\n clearPrice();\n };\n\n const reset = () => {\n setSort(\"recent\");\n setCurrency(\"\");\n setOrderType(\"\");\n clearPrice();\n };\n\n const count = [sort !== \"recent\", currency, orderType, minPrice || maxPrice].filter(Boolean).length;\n\n return {\n open, setOpen,\n sort, setSort,\n currency, selectCurrency,\n orderType, setOrderType,\n minInput, setMinInput,\n maxInput, setMaxInput,\n minPrice, maxPrice,\n clearPrice, reset, count,\n };\n}\n\nexport type MarketplaceFilters = ReturnType<typeof useMarketplaceFilters>;\n\nconst chipClass = (active: boolean) =>\n cn(\n \"inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n active\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n );\n\nfunction ChipGroup({ title, options, value, onSelect }: {\n title: string;\n options: { label: string; value: string; icon?: ReactNode }[];\n value: string;\n onSelect: (value: string) => void;\n}) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">{title}</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {options.map((opt) => (\n <button key={opt.value} onClick={() => onSelect(opt.value)} className={chipClass(value === opt.value)}>\n {opt.icon}\n <span>{opt.label}</span>\n </button>\n ))}\n </div>\n </div>\n );\n}\n\nfunction IpTypeChip({ href, label }: { href: string; label: string }) {\n const pathname = usePathname();\n return (\n <Link\n href={href}\n className={cn(\n \"text-xs px-3 py-1 rounded-full border transition-colors whitespace-nowrap\",\n pathname === href\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {label}\n </Link>\n );\n}\n\nfunction ActiveChip({ children, onClear }: { children: ReactNode; onClear: () => void }) {\n return (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n {children}\n <button onClick={onClear} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n );\n}\n\nexport function FiltersButton({ filters }: { filters: MarketplaceFilters }) {\n return (\n <button\n onClick={() => filters.setOpen(true)}\n aria-label=\"Filters\"\n className={cn(\n \"relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors shrink-0\",\n filters.count > 0\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n {filters.count > 0 && (\n <span className=\"flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground\">\n {filters.count}\n </span>\n )}\n </button>\n );\n}\n\nexport function ActiveFilters({ filters }: { filters: MarketplaceFilters }) {\n const { sort, setSort, orderType, setOrderType, currency, selectCurrency, minPrice, maxPrice, minInput, maxInput, clearPrice, count } = filters;\n if (count === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {sort !== \"recent\" && (\n <ActiveChip onClear={() => setSort(\"recent\")}>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</ActiveChip>\n )}\n {orderType && (\n <ActiveChip onClear={() => setOrderType(\"\")}>{TYPE_OPTIONS.find((o) => o.value === orderType)?.label}</ActiveChip>\n )}\n {currency && (\n <ActiveChip onClear={() => selectCurrency(currency)}>\n <CurrencyIcon symbol={currency} size={12} />\n {currency}\n </ActiveChip>\n )}\n {(minPrice || maxPrice) && (\n <ActiveChip onClear={clearPrice}>{minInput || \"0\"} – {maxInput || \"∞\"}</ActiveChip>\n )}\n </div>\n );\n}\n\nexport function FiltersDialog({ filters }: { filters: MarketplaceFilters }) {\n const { open, setOpen, sort, setSort, orderType, setOrderType, currency, selectCurrency, minInput, setMinInput, maxInput, setMaxInput, reset, count } = filters;\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]\">\n\n <div className=\"flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12\">\n <DialogTitle className=\"text-base font-bold flex items-center gap-2\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </DialogTitle>\n {count > 0 && (\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 text-xs text-muted-foreground\" onClick={reset}>\n Clear all\n </Button>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4 space-y-5\">\n <ChipGroup title=\"Sort\" options={SORT_OPTIONS} value={sort} onSelect={setSort} />\n <ChipGroup title=\"Order type\" options={TYPE_OPTIONS} value={orderType} onSelect={setOrderType} />\n <ChipGroup title=\"Currency\" options={CURRENCY_OPTIONS} value={currency} onSelect={selectCurrency} />\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Price range</p>\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder=\"Min\"\n value={minInput}\n onChange={(e) => setMinInput(e.target.value)}\n className=\"h-9 text-sm\"\n type=\"number\"\n min=\"0\"\n />\n <span className=\"text-sm text-muted-foreground shrink-0\">–</span>\n <Input\n placeholder=\"Max\"\n value={maxInput}\n onChange={(e) => setMaxInput(e.target.value)}\n className=\"h-9 text-sm\"\n type=\"number\"\n min=\"0\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2 pt-1 border-t border-border/60\">\n <p className=\"flex items-center gap-1 text-2xs font-semibold text-muted-foreground\">\n IP Type\n <HelpIcon content=\"Filter by intellectual property category — Art, Audio, Video, Software, and more\" side=\"right\" />\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n <IpTypeChip href=\"/marketplace\" label=\"All\" />\n {IP_TYPES.map((type) => (\n <IpTypeChip key={type} href={`/${type.toLowerCase()}`} label={type} />\n ))}\n </div>\n </div>\n </div>\n\n <div className=\"px-5 py-3 border-t border-border/60\">\n <Button className=\"w-full\" onClick={() => setOpen(false)}>\n Apply filters\n </Button>\n </div>\n\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":["Link"]}
@@ -122,13 +122,13 @@ function FiltersButton({ filters }) {
122
122
  "button",
123
123
  {
124
124
  onClick: () => filters.setOpen(true),
125
+ "aria-label": "Filters",
125
126
  className: cn(
126
127
  "relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors shrink-0",
127
128
  filters.count > 0 ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
128
129
  ),
129
130
  children: [
130
131
  /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
131
- "Filters",
132
132
  filters.count > 0 && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground", children: filters.count })
133
133
  ]
134
134
  }