@medialane/ui 0.83.0 → 0.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/animated-token-media.cjs +100 -0
- package/dist/components/animated-token-media.cjs.map +1 -0
- package/dist/components/animated-token-media.d.cts +28 -0
- package/dist/components/animated-token-media.d.ts +28 -0
- package/dist/components/animated-token-media.js +66 -0
- package/dist/components/animated-token-media.js.map +1 -0
- package/dist/components/asset-card.cjs +14 -13
- package/dist/components/asset-card.cjs.map +1 -1
- package/dist/components/asset-card.d.cts +5 -1
- package/dist/components/asset-card.d.ts +5 -1
- package/dist/components/asset-card.js +14 -13
- package/dist/components/asset-card.js.map +1 -1
- package/dist/components/asset-top-sections.cjs +58 -27
- package/dist/components/asset-top-sections.cjs.map +1 -1
- package/dist/components/asset-top-sections.d.cts +19 -2
- package/dist/components/asset-top-sections.d.ts +19 -2
- package/dist/components/asset-top-sections.js +58 -27
- package/dist/components/asset-top-sections.js.map +1 -1
- package/dist/components/token-card.cjs +15 -13
- package/dist/components/token-card.cjs.map +1 -1
- package/dist/components/token-card.js +15 -13
- package/dist/components/token-card.js.map +1 -1
- package/dist/data/living-render-collections.cjs +43 -0
- package/dist/data/living-render-collections.cjs.map +1 -0
- package/dist/data/living-render-collections.d.cts +16 -0
- package/dist/data/living-render-collections.d.ts +16 -0
- package/dist/data/living-render-collections.js +18 -0
- package/dist/data/living-render-collections.js.map +1 -0
- package/dist/index.cjs +11 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/use-intersection-active.cjs +49 -0
- package/dist/utils/use-intersection-active.cjs.map +1 -0
- package/dist/utils/use-intersection-active.d.cts +12 -0
- package/dist/utils/use-intersection-active.d.ts +12 -0
- package/dist/utils/use-intersection-active.js +25 -0
- package/dist/utils/use-intersection-active.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var animated_token_media_exports = {};
|
|
31
|
+
__export(animated_token_media_exports, {
|
|
32
|
+
AnimatedTokenMedia: () => AnimatedTokenMedia
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(animated_token_media_exports);
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
var import_react = require("react");
|
|
37
|
+
var import_image = __toESM(require("next/image"), 1);
|
|
38
|
+
var import_use_intersection_active = require("../utils/use-intersection-active.js");
|
|
39
|
+
function AnimatedTokenMedia({
|
|
40
|
+
image,
|
|
41
|
+
alt,
|
|
42
|
+
animationUrl,
|
|
43
|
+
live = false,
|
|
44
|
+
mode = "fill",
|
|
45
|
+
className,
|
|
46
|
+
sizes,
|
|
47
|
+
priority,
|
|
48
|
+
imgError: imgErrorProp,
|
|
49
|
+
onImageError,
|
|
50
|
+
fallback
|
|
51
|
+
}) {
|
|
52
|
+
const [internalError, setInternalError] = (0, import_react.useState)(false);
|
|
53
|
+
const imgError = imgErrorProp ?? internalError;
|
|
54
|
+
const handleImageError = () => {
|
|
55
|
+
setInternalError(true);
|
|
56
|
+
onImageError?.();
|
|
57
|
+
};
|
|
58
|
+
const [ref, isVisible] = (0, import_use_intersection_active.useIntersectionActive)();
|
|
59
|
+
const showLive = live && !!animationUrl && isVisible;
|
|
60
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, className: mode === "fill" ? "absolute inset-0" : "w-full", children: showLive ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
61
|
+
"iframe",
|
|
62
|
+
{
|
|
63
|
+
src: animationUrl,
|
|
64
|
+
title: alt,
|
|
65
|
+
sandbox: "allow-scripts",
|
|
66
|
+
loading: "lazy",
|
|
67
|
+
className: mode === "fill" ? "absolute inset-0 w-full h-full border-0" : "w-full aspect-square border-0"
|
|
68
|
+
}
|
|
69
|
+
) : image && !imgError ? mode === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
70
|
+
import_image.default,
|
|
71
|
+
{
|
|
72
|
+
src: image,
|
|
73
|
+
alt,
|
|
74
|
+
fill: true,
|
|
75
|
+
unoptimized: true,
|
|
76
|
+
sizes,
|
|
77
|
+
priority,
|
|
78
|
+
className,
|
|
79
|
+
onError: handleImageError
|
|
80
|
+
}
|
|
81
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
82
|
+
import_image.default,
|
|
83
|
+
{
|
|
84
|
+
src: image,
|
|
85
|
+
alt,
|
|
86
|
+
width: 0,
|
|
87
|
+
height: 0,
|
|
88
|
+
sizes,
|
|
89
|
+
priority,
|
|
90
|
+
crossOrigin: "anonymous",
|
|
91
|
+
className: className ?? "w-full h-auto",
|
|
92
|
+
onError: handleImageError
|
|
93
|
+
}
|
|
94
|
+
) : fallback ?? null });
|
|
95
|
+
}
|
|
96
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
97
|
+
0 && (module.exports = {
|
|
98
|
+
AnimatedTokenMedia
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=animated-token-media.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/animated-token-media.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Image from \"next/image\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\n\nexport interface AnimatedTokenMediaProps {\n /** Resolved (already ipfsToHttp'd, if needed) static image URL. */\n image: string | null;\n alt: string;\n /** Resolved animation_url (data:/http(s)/ipfs-resolved) — the live on-chain renderer. */\n animationUrl?: string | null;\n /** Caller-computed eligibility — this component never decides eligibility itself. */\n live?: boolean;\n /**\n * \"fill\" = absolutely-positioned, fills a `position:relative` parent (grid\n * tiles). \"natural\" = flows at its own width, `w-full h-auto` for the\n * image / a forced square for the live iframe (asset detail hero).\n */\n mode?: \"fill\" | \"natural\";\n className?: string;\n sizes?: string;\n priority?: boolean;\n /** Controlled error state (AssetMediaColumn's existing external contract). Omit for internal (uncontrolled) tracking. */\n imgError?: boolean;\n onImageError?: () => void;\n /** Rendered instead of the image/iframe when there's no image and nothing live to show, or the image errored. */\n fallback?: React.ReactNode;\n}\n\nexport function AnimatedTokenMedia({\n image,\n alt,\n animationUrl,\n live = false,\n mode = \"fill\",\n className,\n sizes,\n priority,\n imgError: imgErrorProp,\n onImageError,\n fallback,\n}: AnimatedTokenMediaProps) {\n const [internalError, setInternalError] = useState(false);\n const imgError = imgErrorProp ?? internalError;\n const handleImageError = () => {\n setInternalError(true);\n onImageError?.();\n };\n\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const showLive = live && !!animationUrl && isVisible;\n\n return (\n <div ref={ref} className={mode === \"fill\" ? \"absolute inset-0\" : \"w-full\"}>\n {showLive ? (\n <iframe\n src={animationUrl!}\n title={alt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className={\n mode === \"fill\"\n ? \"absolute inset-0 w-full h-full border-0\"\n : \"w-full aspect-square border-0\"\n }\n />\n ) : image && !imgError ? (\n mode === \"fill\" ? (\n <Image\n src={image}\n alt={alt}\n fill\n unoptimized\n sizes={sizes}\n priority={priority}\n className={className}\n onError={handleImageError}\n />\n ) : (\n <Image\n src={image}\n alt={alt}\n width={0}\n height={0}\n sizes={sizes}\n priority={priority}\n crossOrigin=\"anonymous\"\n className={className ?? \"w-full h-auto\"}\n onError={handleImageError}\n />\n )\n ) : (\n fallback ?? null\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwDQ;AAtDR,mBAAyB;AACzB,mBAAkB;AAClB,qCAAsC;AA0B/B,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAS,KAAK;AACxD,QAAM,WAAW,gBAAgB;AACjC,QAAM,mBAAmB,MAAM;AAC7B,qBAAiB,IAAI;AACrB,mBAAe;AAAA,EACjB;AAEA,QAAM,CAAC,KAAK,SAAS,QAAI,sDAAsC;AAC/D,QAAM,WAAW,QAAQ,CAAC,CAAC,gBAAgB;AAE3C,SACE,4CAAC,SAAI,KAAU,WAAW,SAAS,SAAS,qBAAqB,UAC9D,qBACC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,OAAO;AAAA,MACP,SAAQ;AAAA,MACR,SAAQ;AAAA,MACR,WACE,SAAS,SACL,4CACA;AAAA;AAAA,EAER,IACE,SAAS,CAAC,WACZ,SAAS,SACP;AAAA,IAAC,aAAAA;AAAA,IAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,MAAI;AAAA,MACJ,aAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA;AAAA,EACX,IAEA;AAAA,IAAC,aAAAA;AAAA,IAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,aAAY;AAAA,MACZ,WAAW,aAAa;AAAA,MACxB,SAAS;AAAA;AAAA,EACX,IAGF,YAAY,MAEhB;AAEJ;","names":["Image"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface AnimatedTokenMediaProps {
|
|
4
|
+
/** Resolved (already ipfsToHttp'd, if needed) static image URL. */
|
|
5
|
+
image: string | null;
|
|
6
|
+
alt: string;
|
|
7
|
+
/** Resolved animation_url (data:/http(s)/ipfs-resolved) — the live on-chain renderer. */
|
|
8
|
+
animationUrl?: string | null;
|
|
9
|
+
/** Caller-computed eligibility — this component never decides eligibility itself. */
|
|
10
|
+
live?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* "fill" = absolutely-positioned, fills a `position:relative` parent (grid
|
|
13
|
+
* tiles). "natural" = flows at its own width, `w-full h-auto` for the
|
|
14
|
+
* image / a forced square for the live iframe (asset detail hero).
|
|
15
|
+
*/
|
|
16
|
+
mode?: "fill" | "natural";
|
|
17
|
+
className?: string;
|
|
18
|
+
sizes?: string;
|
|
19
|
+
priority?: boolean;
|
|
20
|
+
/** Controlled error state (AssetMediaColumn's existing external contract). Omit for internal (uncontrolled) tracking. */
|
|
21
|
+
imgError?: boolean;
|
|
22
|
+
onImageError?: () => void;
|
|
23
|
+
/** Rendered instead of the image/iframe when there's no image and nothing live to show, or the image errored. */
|
|
24
|
+
fallback?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
declare function AnimatedTokenMedia({ image, alt, animationUrl, live, mode, className, sizes, priority, imgError: imgErrorProp, onImageError, fallback, }: AnimatedTokenMediaProps): react_jsx_runtime.JSX.Element;
|
|
27
|
+
|
|
28
|
+
export { AnimatedTokenMedia, type AnimatedTokenMediaProps };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface AnimatedTokenMediaProps {
|
|
4
|
+
/** Resolved (already ipfsToHttp'd, if needed) static image URL. */
|
|
5
|
+
image: string | null;
|
|
6
|
+
alt: string;
|
|
7
|
+
/** Resolved animation_url (data:/http(s)/ipfs-resolved) — the live on-chain renderer. */
|
|
8
|
+
animationUrl?: string | null;
|
|
9
|
+
/** Caller-computed eligibility — this component never decides eligibility itself. */
|
|
10
|
+
live?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* "fill" = absolutely-positioned, fills a `position:relative` parent (grid
|
|
13
|
+
* tiles). "natural" = flows at its own width, `w-full h-auto` for the
|
|
14
|
+
* image / a forced square for the live iframe (asset detail hero).
|
|
15
|
+
*/
|
|
16
|
+
mode?: "fill" | "natural";
|
|
17
|
+
className?: string;
|
|
18
|
+
sizes?: string;
|
|
19
|
+
priority?: boolean;
|
|
20
|
+
/** Controlled error state (AssetMediaColumn's existing external contract). Omit for internal (uncontrolled) tracking. */
|
|
21
|
+
imgError?: boolean;
|
|
22
|
+
onImageError?: () => void;
|
|
23
|
+
/** Rendered instead of the image/iframe when there's no image and nothing live to show, or the image errored. */
|
|
24
|
+
fallback?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
declare function AnimatedTokenMedia({ image, alt, animationUrl, live, mode, className, sizes, priority, imgError: imgErrorProp, onImageError, fallback, }: AnimatedTokenMediaProps): react_jsx_runtime.JSX.Element;
|
|
27
|
+
|
|
28
|
+
export { AnimatedTokenMedia, type AnimatedTokenMediaProps };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import Image from "next/image";
|
|
5
|
+
import { useIntersectionActive } from "../utils/use-intersection-active.js";
|
|
6
|
+
function AnimatedTokenMedia({
|
|
7
|
+
image,
|
|
8
|
+
alt,
|
|
9
|
+
animationUrl,
|
|
10
|
+
live = false,
|
|
11
|
+
mode = "fill",
|
|
12
|
+
className,
|
|
13
|
+
sizes,
|
|
14
|
+
priority,
|
|
15
|
+
imgError: imgErrorProp,
|
|
16
|
+
onImageError,
|
|
17
|
+
fallback
|
|
18
|
+
}) {
|
|
19
|
+
const [internalError, setInternalError] = useState(false);
|
|
20
|
+
const imgError = imgErrorProp ?? internalError;
|
|
21
|
+
const handleImageError = () => {
|
|
22
|
+
setInternalError(true);
|
|
23
|
+
onImageError?.();
|
|
24
|
+
};
|
|
25
|
+
const [ref, isVisible] = useIntersectionActive();
|
|
26
|
+
const showLive = live && !!animationUrl && isVisible;
|
|
27
|
+
return /* @__PURE__ */ jsx("div", { ref, className: mode === "fill" ? "absolute inset-0" : "w-full", children: showLive ? /* @__PURE__ */ jsx(
|
|
28
|
+
"iframe",
|
|
29
|
+
{
|
|
30
|
+
src: animationUrl,
|
|
31
|
+
title: alt,
|
|
32
|
+
sandbox: "allow-scripts",
|
|
33
|
+
loading: "lazy",
|
|
34
|
+
className: mode === "fill" ? "absolute inset-0 w-full h-full border-0" : "w-full aspect-square border-0"
|
|
35
|
+
}
|
|
36
|
+
) : image && !imgError ? mode === "fill" ? /* @__PURE__ */ jsx(
|
|
37
|
+
Image,
|
|
38
|
+
{
|
|
39
|
+
src: image,
|
|
40
|
+
alt,
|
|
41
|
+
fill: true,
|
|
42
|
+
unoptimized: true,
|
|
43
|
+
sizes,
|
|
44
|
+
priority,
|
|
45
|
+
className,
|
|
46
|
+
onError: handleImageError
|
|
47
|
+
}
|
|
48
|
+
) : /* @__PURE__ */ jsx(
|
|
49
|
+
Image,
|
|
50
|
+
{
|
|
51
|
+
src: image,
|
|
52
|
+
alt,
|
|
53
|
+
width: 0,
|
|
54
|
+
height: 0,
|
|
55
|
+
sizes,
|
|
56
|
+
priority,
|
|
57
|
+
crossOrigin: "anonymous",
|
|
58
|
+
className: className ?? "w-full h-auto",
|
|
59
|
+
onError: handleImageError
|
|
60
|
+
}
|
|
61
|
+
) : fallback ?? null });
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
AnimatedTokenMedia
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=animated-token-media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/animated-token-media.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Image from \"next/image\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\n\nexport interface AnimatedTokenMediaProps {\n /** Resolved (already ipfsToHttp'd, if needed) static image URL. */\n image: string | null;\n alt: string;\n /** Resolved animation_url (data:/http(s)/ipfs-resolved) — the live on-chain renderer. */\n animationUrl?: string | null;\n /** Caller-computed eligibility — this component never decides eligibility itself. */\n live?: boolean;\n /**\n * \"fill\" = absolutely-positioned, fills a `position:relative` parent (grid\n * tiles). \"natural\" = flows at its own width, `w-full h-auto` for the\n * image / a forced square for the live iframe (asset detail hero).\n */\n mode?: \"fill\" | \"natural\";\n className?: string;\n sizes?: string;\n priority?: boolean;\n /** Controlled error state (AssetMediaColumn's existing external contract). Omit for internal (uncontrolled) tracking. */\n imgError?: boolean;\n onImageError?: () => void;\n /** Rendered instead of the image/iframe when there's no image and nothing live to show, or the image errored. */\n fallback?: React.ReactNode;\n}\n\nexport function AnimatedTokenMedia({\n image,\n alt,\n animationUrl,\n live = false,\n mode = \"fill\",\n className,\n sizes,\n priority,\n imgError: imgErrorProp,\n onImageError,\n fallback,\n}: AnimatedTokenMediaProps) {\n const [internalError, setInternalError] = useState(false);\n const imgError = imgErrorProp ?? internalError;\n const handleImageError = () => {\n setInternalError(true);\n onImageError?.();\n };\n\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const showLive = live && !!animationUrl && isVisible;\n\n return (\n <div ref={ref} className={mode === \"fill\" ? \"absolute inset-0\" : \"w-full\"}>\n {showLive ? (\n <iframe\n src={animationUrl!}\n title={alt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className={\n mode === \"fill\"\n ? \"absolute inset-0 w-full h-full border-0\"\n : \"w-full aspect-square border-0\"\n }\n />\n ) : image && !imgError ? (\n mode === \"fill\" ? (\n <Image\n src={image}\n alt={alt}\n fill\n unoptimized\n sizes={sizes}\n priority={priority}\n className={className}\n onError={handleImageError}\n />\n ) : (\n <Image\n src={image}\n alt={alt}\n width={0}\n height={0}\n sizes={sizes}\n priority={priority}\n crossOrigin=\"anonymous\"\n className={className ?? \"w-full h-auto\"}\n onError={handleImageError}\n />\n )\n ) : (\n fallback ?? null\n )}\n </div>\n );\n}\n"],"mappings":";AAwDQ;AAtDR,SAAS,gBAAgB;AACzB,OAAO,WAAW;AAClB,SAAS,6BAA6B;AA0B/B,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AACxD,QAAM,WAAW,gBAAgB;AACjC,QAAM,mBAAmB,MAAM;AAC7B,qBAAiB,IAAI;AACrB,mBAAe;AAAA,EACjB;AAEA,QAAM,CAAC,KAAK,SAAS,IAAI,sBAAsC;AAC/D,QAAM,WAAW,QAAQ,CAAC,CAAC,gBAAgB;AAE3C,SACE,oBAAC,SAAI,KAAU,WAAW,SAAS,SAAS,qBAAqB,UAC9D,qBACC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,OAAO;AAAA,MACP,SAAQ;AAAA,MACR,SAAQ;AAAA,MACR,WACE,SAAS,SACL,4CACA;AAAA;AAAA,EAER,IACE,SAAS,CAAC,WACZ,SAAS,SACP;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,MAAI;AAAA,MACJ,aAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA;AAAA,EACX,IAEA;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,aAAY;AAAA,MACZ,WAAW,aAAa;AAAA,MACxB,SAAS;AAAA;AAAA,EACX,IAGF,YAAY,MAEhB;AAEJ;","names":[]}
|
|
@@ -34,19 +34,20 @@ __export(asset_card_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(asset_card_exports);
|
|
36
36
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
37
|
-
var import_react = require("react");
|
|
38
37
|
var import_link = __toESM(require("next/link"), 1);
|
|
39
|
-
var import_image = __toESM(require("next/image"), 1);
|
|
40
38
|
var import_lucide_react = require("lucide-react");
|
|
41
39
|
var import_cn = require("../utils/cn.js");
|
|
42
40
|
var import_ipfs = require("../utils/ipfs.js");
|
|
43
41
|
var import_format = require("../utils/format.js");
|
|
44
42
|
var import_currency_icon = require("./currency-icon.js");
|
|
45
43
|
var import_ip_type_badge = require("./ip-type-badge.js");
|
|
44
|
+
var import_animated_token_media = require("./animated-token-media.js");
|
|
46
45
|
function AssetCard({
|
|
47
46
|
href,
|
|
48
47
|
name,
|
|
49
48
|
image,
|
|
49
|
+
animationUrl,
|
|
50
|
+
live = false,
|
|
50
51
|
subtitle,
|
|
51
52
|
ipType,
|
|
52
53
|
price,
|
|
@@ -55,7 +56,6 @@ function AssetCard({
|
|
|
55
56
|
ipTypeBaseUrl = "",
|
|
56
57
|
className
|
|
57
58
|
}) {
|
|
58
|
-
const [imgError, setImgError] = (0, import_react.useState)(false);
|
|
59
59
|
const resolved = image ? (0, import_ipfs.ipfsToHttp)(image) : null;
|
|
60
60
|
const hasPrice = !!price?.formatted;
|
|
61
61
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -67,21 +67,22 @@ function AssetCard({
|
|
|
67
67
|
),
|
|
68
68
|
children: [
|
|
69
69
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href, className: "block p-1.5 pb-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative aspect-[4/5] rounded-[12px] bg-muted overflow-hidden ring-1 ring-border/50", children: [
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
71
|
+
import_animated_token_media.AnimatedTokenMedia,
|
|
72
72
|
{
|
|
73
|
-
|
|
73
|
+
image: resolved,
|
|
74
|
+
animationUrl,
|
|
75
|
+
live,
|
|
74
76
|
alt: name,
|
|
75
|
-
|
|
76
|
-
unoptimized: true,
|
|
77
|
+
mode: "fill",
|
|
77
78
|
sizes: "(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw",
|
|
78
79
|
className: "object-cover transition-transform duration-500 group-hover:scale-105",
|
|
79
|
-
|
|
80
|
+
fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-blue/25 via-brand-purple/25 to-brand-rose/25", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-sm font-semibold text-muted-foreground tabular-nums", children: [
|
|
81
|
+
"#",
|
|
82
|
+
fallbackId ?? "?"
|
|
83
|
+
] }) })
|
|
80
84
|
}
|
|
81
|
-
)
|
|
82
|
-
"#",
|
|
83
|
-
fallbackId ?? "?"
|
|
84
|
-
] }) }),
|
|
85
|
+
),
|
|
85
86
|
hasPrice && !indexing && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "absolute bottom-2 right-2 inline-flex items-center gap-1 text-xs font-bold text-white/95 backdrop-blur-md bg-black/40 rounded-full px-2.5 py-1 tabular-nums", children: [
|
|
86
87
|
price.currency && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_currency_icon.CurrencyIcon, { symbol: price.currency, size: 12 }),
|
|
87
88
|
(0, import_format.formatDisplayPrice)(price.formatted)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/asset-card.tsx"],"sourcesContent":["\"use client\";\n\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/components/asset-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { Loader2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\n\nexport interface AssetCardPrice {\n formatted?: string | null;\n currency?: string | null;\n}\n\nexport interface AssetCardProps {\n /** Asset detail link. */\n href: string;\n /** Asset title. */\n name: string;\n /** Raw image (ipfs:// or http) — resolved internally. */\n image?: string | null;\n /** Resolved animation_url — the live on-chain renderer, if any. */\n animationUrl?: string | null;\n /** Caller-computed eligibility for the living-render treatment. */\n live?: boolean;\n /** Secondary line under the title (collection or description). */\n subtitle?: string | null;\n /** IP type (apiValue or label) — renders the icon badge on the footer left. */\n ipType?: string | null;\n /** Listing price; omit/null shows no price. */\n price?: AssetCardPrice | null;\n /** Shown when the image is missing/errors (e.g. token id). */\n fallbackId?: string | null;\n /** Show the \"Indexing…\" overlay. */\n indexing?: boolean;\n /** Base URL for the ipType badge link. Default \"\" (relative). */\n ipTypeBaseUrl?: string;\n className?: string;\n}\n\n/**\n * The platform's shared asset card — lean and presentational.\n *\n * Intentionally has no action buttons, dropdowns or dialogs: the image and\n * text are the only interactive surface (a single asset link), which keeps\n * browse grids and carousels fast even when many cards render. Surfaces that\n * need owner/marketplace actions use the richer `TokenCard` / `ListingCard`.\n *\n * Layout: inset 4:5 artwork (gallery ratio, echoing the Medialane Collection\n * Card) with the price as a glass pill on the artwork (the collection cards'\n * Floor-pill vocabulary) → one body row: display-face title + optional\n * subtitle, with the optional ipType badge inline on the right.\n */\nexport function AssetCard({\n href,\n name,\n image,\n animationUrl,\n live = false,\n subtitle,\n ipType,\n price,\n fallbackId,\n indexing = false,\n ipTypeBaseUrl = \"\",\n className,\n}: AssetCardProps) {\n const resolved = image ? ipfsToHttp(image) : null;\n const hasPrice = !!price?.formatted;\n\n return (\n <div\n className={cn(\n \"card-base group relative flex flex-col w-full transition-colors hover:border-foreground/20\",\n className\n )}\n >\n {/* Artwork — inset with a gallery 4:5 ratio, like the Collection Card */}\n <Link href={href} className=\"block p-1.5 pb-0\">\n <div className=\"relative aspect-[4/5] rounded-[12px] bg-muted overflow-hidden ring-1 ring-border/50\">\n <AnimatedTokenMedia\n image={resolved}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n fallback={\n <div className=\"absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-blue/25 via-brand-purple/25 to-brand-rose/25\">\n <span className=\"text-sm font-semibold text-muted-foreground tabular-nums\">\n #{fallbackId ?? \"?\"}\n </span>\n </div>\n }\n />\n\n {/* Price pill — anchored on the artwork, same glass chip as the\n collection cards' Floor pill */}\n {hasPrice && !indexing && (\n <span className=\"absolute bottom-2 right-2 inline-flex items-center gap-1 text-xs font-bold text-white/95 backdrop-blur-md bg-black/40 rounded-full px-2.5 py-1 tabular-nums\">\n {price!.currency && <CurrencyIcon symbol={price!.currency} size={12} />}\n {formatDisplayPrice(price!.formatted!)}\n </span>\n )}\n\n {indexing && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-[10px] text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n </Link>\n\n {/* Body — one tight block: title (+ optional ipType badge inline) and subtitle */}\n <div className=\"flex items-start justify-between gap-2 px-3 py-3\">\n <Link href={href} className=\"block min-w-0 flex-1\">\n <p className=\"text-[15px] font-bold line-clamp-1 leading-snug\">\n {name}\n </p>\n {subtitle && (\n <p className=\"text-xs text-muted-foreground line-clamp-1 leading-snug\">\n {subtitle}\n </p>\n )}\n </Link>\n\n {ipType && (\n <div className=\"shrink-0 mt-0.5\">\n <IpTypeBadge ipType={ipType} size=\"sm\" baseUrl={ipTypeBaseUrl} />\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport function AssetCardSkeleton() {\n return (\n <div className=\"card-base flex flex-col w-full\">\n <div className=\"p-1.5 pb-0\">\n <div className=\"aspect-[4/5] w-full animate-pulse bg-muted rounded-[12px]\" />\n </div>\n <div className=\"px-3 py-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n <div className=\"h-3.5 w-2/5 rounded-md animate-pulse bg-muted\" />\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2Fc;AAzFd,kBAAiB;AACjB,0BAAwB;AACxB,gBAAmB;AACnB,kBAA2B;AAC3B,oBAAmC;AACnC,2BAA6B;AAC7B,2BAA4B;AAC5B,kCAAmC;AA8C5B,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB;AACF,GAAmB;AACjB,QAAM,WAAW,YAAQ,wBAAW,KAAK,IAAI;AAC7C,QAAM,WAAW,CAAC,CAAC,OAAO;AAE1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAGA;AAAA,oDAAC,YAAAA,SAAA,EAAK,MAAY,WAAU,oBAC1B,uDAAC,SAAI,WAAU,uFACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,KAAK;AAAA,cACL,MAAK;AAAA,cACL,OAAM;AAAA,cACN,WAAU;AAAA,cACV,UACE,4CAAC,SAAI,WAAU,+HACb,uDAAC,UAAK,WAAU,4DAA2D;AAAA;AAAA,gBACvE,cAAc;AAAA,iBAClB,GACF;AAAA;AAAA,UAEJ;AAAA,UAIC,YAAY,CAAC,YACZ,6CAAC,UAAK,WAAU,+JACb;AAAA,kBAAO,YAAY,4CAAC,qCAAa,QAAQ,MAAO,UAAU,MAAM,IAAI;AAAA,gBACpE,kCAAmB,MAAO,SAAU;AAAA,aACvC;AAAA,UAGD,YACC,6CAAC,SAAI,WAAU,4GACb;AAAA,wDAAC,+BAAQ,WAAU,sCAAqC;AAAA,YACxD,4CAAC,UAAK,WAAU,6BAA4B,4BAAS;AAAA,aACvD;AAAA,WAEJ,GACF;AAAA,QAGA,6CAAC,SAAI,WAAU,oDACb;AAAA,uDAAC,YAAAA,SAAA,EAAK,MAAY,WAAU,wBAC1B;AAAA,wDAAC,OAAE,WAAU,mDACV,gBACH;AAAA,YACC,YACC,4CAAC,OAAE,WAAU,2DACV,oBACH;AAAA,aAEJ;AAAA,UAEC,UACC,4CAAC,SAAI,WAAU,mBACb,sDAAC,oCAAY,QAAgB,MAAK,MAAK,SAAS,eAAe,GACjE;AAAA,WAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,oBAAoB;AAClC,SACE,6CAAC,SAAI,WAAU,kCACb;AAAA,gDAAC,SAAI,WAAU,cACb,sDAAC,SAAI,WAAU,6DAA4D,GAC7E;AAAA,IACA,6CAAC,SAAI,WAAU,yBACb;AAAA,kDAAC,SAAI,WAAU,+CAA8C;AAAA,MAC7D,4CAAC,SAAI,WAAU,iDAAgD;AAAA,OACjE;AAAA,KACF;AAEJ;","names":["Link"]}
|
|
@@ -11,6 +11,10 @@ interface AssetCardProps {
|
|
|
11
11
|
name: string;
|
|
12
12
|
/** Raw image (ipfs:// or http) — resolved internally. */
|
|
13
13
|
image?: string | null;
|
|
14
|
+
/** Resolved animation_url — the live on-chain renderer, if any. */
|
|
15
|
+
animationUrl?: string | null;
|
|
16
|
+
/** Caller-computed eligibility for the living-render treatment. */
|
|
17
|
+
live?: boolean;
|
|
14
18
|
/** Secondary line under the title (collection or description). */
|
|
15
19
|
subtitle?: string | null;
|
|
16
20
|
/** IP type (apiValue or label) — renders the icon badge on the footer left. */
|
|
@@ -38,7 +42,7 @@ interface AssetCardProps {
|
|
|
38
42
|
* Floor-pill vocabulary) → one body row: display-face title + optional
|
|
39
43
|
* subtitle, with the optional ipType badge inline on the right.
|
|
40
44
|
*/
|
|
41
|
-
declare function AssetCard({ href, name, image, subtitle, ipType, price, fallbackId, indexing, ipTypeBaseUrl, className, }: AssetCardProps): react_jsx_runtime.JSX.Element;
|
|
45
|
+
declare function AssetCard({ href, name, image, animationUrl, live, subtitle, ipType, price, fallbackId, indexing, ipTypeBaseUrl, className, }: AssetCardProps): react_jsx_runtime.JSX.Element;
|
|
42
46
|
declare function AssetCardSkeleton(): react_jsx_runtime.JSX.Element;
|
|
43
47
|
|
|
44
48
|
export { AssetCard, type AssetCardPrice, type AssetCardProps, AssetCardSkeleton };
|
|
@@ -11,6 +11,10 @@ interface AssetCardProps {
|
|
|
11
11
|
name: string;
|
|
12
12
|
/** Raw image (ipfs:// or http) — resolved internally. */
|
|
13
13
|
image?: string | null;
|
|
14
|
+
/** Resolved animation_url — the live on-chain renderer, if any. */
|
|
15
|
+
animationUrl?: string | null;
|
|
16
|
+
/** Caller-computed eligibility for the living-render treatment. */
|
|
17
|
+
live?: boolean;
|
|
14
18
|
/** Secondary line under the title (collection or description). */
|
|
15
19
|
subtitle?: string | null;
|
|
16
20
|
/** IP type (apiValue or label) — renders the icon badge on the footer left. */
|
|
@@ -38,7 +42,7 @@ interface AssetCardProps {
|
|
|
38
42
|
* Floor-pill vocabulary) → one body row: display-face title + optional
|
|
39
43
|
* subtitle, with the optional ipType badge inline on the right.
|
|
40
44
|
*/
|
|
41
|
-
declare function AssetCard({ href, name, image, subtitle, ipType, price, fallbackId, indexing, ipTypeBaseUrl, className, }: AssetCardProps): react_jsx_runtime.JSX.Element;
|
|
45
|
+
declare function AssetCard({ href, name, image, animationUrl, live, subtitle, ipType, price, fallbackId, indexing, ipTypeBaseUrl, className, }: AssetCardProps): react_jsx_runtime.JSX.Element;
|
|
42
46
|
declare function AssetCardSkeleton(): react_jsx_runtime.JSX.Element;
|
|
43
47
|
|
|
44
48
|
export { AssetCard, type AssetCardPrice, type AssetCardProps, AssetCardSkeleton };
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
4
3
|
import Link from "next/link";
|
|
5
|
-
import Image from "next/image";
|
|
6
4
|
import { Loader2 } from "lucide-react";
|
|
7
5
|
import { cn } from "../utils/cn.js";
|
|
8
6
|
import { ipfsToHttp } from "../utils/ipfs.js";
|
|
9
7
|
import { formatDisplayPrice } from "../utils/format.js";
|
|
10
8
|
import { CurrencyIcon } from "./currency-icon.js";
|
|
11
9
|
import { IpTypeBadge } from "./ip-type-badge.js";
|
|
10
|
+
import { AnimatedTokenMedia } from "./animated-token-media.js";
|
|
12
11
|
function AssetCard({
|
|
13
12
|
href,
|
|
14
13
|
name,
|
|
15
14
|
image,
|
|
15
|
+
animationUrl,
|
|
16
|
+
live = false,
|
|
16
17
|
subtitle,
|
|
17
18
|
ipType,
|
|
18
19
|
price,
|
|
@@ -21,7 +22,6 @@ function AssetCard({
|
|
|
21
22
|
ipTypeBaseUrl = "",
|
|
22
23
|
className
|
|
23
24
|
}) {
|
|
24
|
-
const [imgError, setImgError] = useState(false);
|
|
25
25
|
const resolved = image ? ipfsToHttp(image) : null;
|
|
26
26
|
const hasPrice = !!price?.formatted;
|
|
27
27
|
return /* @__PURE__ */ jsxs(
|
|
@@ -33,21 +33,22 @@ function AssetCard({
|
|
|
33
33
|
),
|
|
34
34
|
children: [
|
|
35
35
|
/* @__PURE__ */ jsx(Link, { href, className: "block p-1.5 pb-0", children: /* @__PURE__ */ jsxs("div", { className: "relative aspect-[4/5] rounded-[12px] bg-muted overflow-hidden ring-1 ring-border/50", children: [
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
AnimatedTokenMedia,
|
|
38
38
|
{
|
|
39
|
-
|
|
39
|
+
image: resolved,
|
|
40
|
+
animationUrl,
|
|
41
|
+
live,
|
|
40
42
|
alt: name,
|
|
41
|
-
|
|
42
|
-
unoptimized: true,
|
|
43
|
+
mode: "fill",
|
|
43
44
|
sizes: "(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw",
|
|
44
45
|
className: "object-cover transition-transform duration-500 group-hover:scale-105",
|
|
45
|
-
|
|
46
|
+
fallback: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-blue/25 via-brand-purple/25 to-brand-rose/25", children: /* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold text-muted-foreground tabular-nums", children: [
|
|
47
|
+
"#",
|
|
48
|
+
fallbackId ?? "?"
|
|
49
|
+
] }) })
|
|
46
50
|
}
|
|
47
|
-
)
|
|
48
|
-
"#",
|
|
49
|
-
fallbackId ?? "?"
|
|
50
|
-
] }) }),
|
|
51
|
+
),
|
|
51
52
|
hasPrice && !indexing && /* @__PURE__ */ jsxs("span", { className: "absolute bottom-2 right-2 inline-flex items-center gap-1 text-xs font-bold text-white/95 backdrop-blur-md bg-black/40 rounded-full px-2.5 py-1 tabular-nums", children: [
|
|
52
53
|
price.currency && /* @__PURE__ */ jsx(CurrencyIcon, { symbol: price.currency, size: 12 }),
|
|
53
54
|
formatDisplayPrice(price.formatted)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/asset-card.tsx"],"sourcesContent":["\"use client\";\n\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/components/asset-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { Loader2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\n\nexport interface AssetCardPrice {\n formatted?: string | null;\n currency?: string | null;\n}\n\nexport interface AssetCardProps {\n /** Asset detail link. */\n href: string;\n /** Asset title. */\n name: string;\n /** Raw image (ipfs:// or http) — resolved internally. */\n image?: string | null;\n /** Resolved animation_url — the live on-chain renderer, if any. */\n animationUrl?: string | null;\n /** Caller-computed eligibility for the living-render treatment. */\n live?: boolean;\n /** Secondary line under the title (collection or description). */\n subtitle?: string | null;\n /** IP type (apiValue or label) — renders the icon badge on the footer left. */\n ipType?: string | null;\n /** Listing price; omit/null shows no price. */\n price?: AssetCardPrice | null;\n /** Shown when the image is missing/errors (e.g. token id). */\n fallbackId?: string | null;\n /** Show the \"Indexing…\" overlay. */\n indexing?: boolean;\n /** Base URL for the ipType badge link. Default \"\" (relative). */\n ipTypeBaseUrl?: string;\n className?: string;\n}\n\n/**\n * The platform's shared asset card — lean and presentational.\n *\n * Intentionally has no action buttons, dropdowns or dialogs: the image and\n * text are the only interactive surface (a single asset link), which keeps\n * browse grids and carousels fast even when many cards render. Surfaces that\n * need owner/marketplace actions use the richer `TokenCard` / `ListingCard`.\n *\n * Layout: inset 4:5 artwork (gallery ratio, echoing the Medialane Collection\n * Card) with the price as a glass pill on the artwork (the collection cards'\n * Floor-pill vocabulary) → one body row: display-face title + optional\n * subtitle, with the optional ipType badge inline on the right.\n */\nexport function AssetCard({\n href,\n name,\n image,\n animationUrl,\n live = false,\n subtitle,\n ipType,\n price,\n fallbackId,\n indexing = false,\n ipTypeBaseUrl = \"\",\n className,\n}: AssetCardProps) {\n const resolved = image ? ipfsToHttp(image) : null;\n const hasPrice = !!price?.formatted;\n\n return (\n <div\n className={cn(\n \"card-base group relative flex flex-col w-full transition-colors hover:border-foreground/20\",\n className\n )}\n >\n {/* Artwork — inset with a gallery 4:5 ratio, like the Collection Card */}\n <Link href={href} className=\"block p-1.5 pb-0\">\n <div className=\"relative aspect-[4/5] rounded-[12px] bg-muted overflow-hidden ring-1 ring-border/50\">\n <AnimatedTokenMedia\n image={resolved}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n fallback={\n <div className=\"absolute inset-0 flex items-center justify-center bg-gradient-to-br from-brand-blue/25 via-brand-purple/25 to-brand-rose/25\">\n <span className=\"text-sm font-semibold text-muted-foreground tabular-nums\">\n #{fallbackId ?? \"?\"}\n </span>\n </div>\n }\n />\n\n {/* Price pill — anchored on the artwork, same glass chip as the\n collection cards' Floor pill */}\n {hasPrice && !indexing && (\n <span className=\"absolute bottom-2 right-2 inline-flex items-center gap-1 text-xs font-bold text-white/95 backdrop-blur-md bg-black/40 rounded-full px-2.5 py-1 tabular-nums\">\n {price!.currency && <CurrencyIcon symbol={price!.currency} size={12} />}\n {formatDisplayPrice(price!.formatted!)}\n </span>\n )}\n\n {indexing && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-[10px] text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n </Link>\n\n {/* Body — one tight block: title (+ optional ipType badge inline) and subtitle */}\n <div className=\"flex items-start justify-between gap-2 px-3 py-3\">\n <Link href={href} className=\"block min-w-0 flex-1\">\n <p className=\"text-[15px] font-bold line-clamp-1 leading-snug\">\n {name}\n </p>\n {subtitle && (\n <p className=\"text-xs text-muted-foreground line-clamp-1 leading-snug\">\n {subtitle}\n </p>\n )}\n </Link>\n\n {ipType && (\n <div className=\"shrink-0 mt-0.5\">\n <IpTypeBadge ipType={ipType} size=\"sm\" baseUrl={ipTypeBaseUrl} />\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport function AssetCardSkeleton() {\n return (\n <div className=\"card-base flex flex-col w-full\">\n <div className=\"p-1.5 pb-0\">\n <div className=\"aspect-[4/5] w-full animate-pulse bg-muted rounded-[12px]\" />\n </div>\n <div className=\"px-3 py-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n <div className=\"h-3.5 w-2/5 rounded-md animate-pulse bg-muted\" />\n </div>\n </div>\n );\n}\n"],"mappings":";AA2Fc,cACE,YADF;AAzFd,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,SAAS,UAAU;AACnB,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,0BAA0B;AA8C5B,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB;AACF,GAAmB;AACjB,QAAM,WAAW,QAAQ,WAAW,KAAK,IAAI;AAC7C,QAAM,WAAW,CAAC,CAAC,OAAO;AAE1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAGA;AAAA,4BAAC,QAAK,MAAY,WAAU,oBAC1B,+BAAC,SAAI,WAAU,uFACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,KAAK;AAAA,cACL,MAAK;AAAA,cACL,OAAM;AAAA,cACN,WAAU;AAAA,cACV,UACE,oBAAC,SAAI,WAAU,+HACb,+BAAC,UAAK,WAAU,4DAA2D;AAAA;AAAA,gBACvE,cAAc;AAAA,iBAClB,GACF;AAAA;AAAA,UAEJ;AAAA,UAIC,YAAY,CAAC,YACZ,qBAAC,UAAK,WAAU,+JACb;AAAA,kBAAO,YAAY,oBAAC,gBAAa,QAAQ,MAAO,UAAU,MAAM,IAAI;AAAA,YACpE,mBAAmB,MAAO,SAAU;AAAA,aACvC;AAAA,UAGD,YACC,qBAAC,SAAI,WAAU,4GACb;AAAA,gCAAC,WAAQ,WAAU,sCAAqC;AAAA,YACxD,oBAAC,UAAK,WAAU,6BAA4B,4BAAS;AAAA,aACvD;AAAA,WAEJ,GACF;AAAA,QAGA,qBAAC,SAAI,WAAU,oDACb;AAAA,+BAAC,QAAK,MAAY,WAAU,wBAC1B;AAAA,gCAAC,OAAE,WAAU,mDACV,gBACH;AAAA,YACC,YACC,oBAAC,OAAE,WAAU,2DACV,oBACH;AAAA,aAEJ;AAAA,UAEC,UACC,oBAAC,SAAI,WAAU,mBACb,8BAAC,eAAY,QAAgB,MAAK,MAAK,SAAS,eAAe,GACjE;AAAA,WAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,oBAAoB;AAClC,SACE,qBAAC,SAAI,WAAU,kCACb;AAAA,wBAAC,SAAI,WAAU,cACb,8BAAC,SAAI,WAAU,6DAA4D,GAC7E;AAAA,IACA,qBAAC,SAAI,WAAU,yBACb;AAAA,0BAAC,SAAI,WAAU,+CAA8C;AAAA,MAC7D,oBAAC,SAAI,WAAU,iDAAgD;AAAA,OACjE;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -36,9 +36,9 @@ __export(asset_top_sections_exports, {
|
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(asset_top_sections_exports);
|
|
38
38
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
|
-
var import_image = __toESM(require("next/image"), 1);
|
|
40
39
|
var import_link = __toESM(require("next/link"), 1);
|
|
41
40
|
var import_framer_motion = require("framer-motion");
|
|
41
|
+
var import_use_intersection_active = require("../utils/use-intersection-active.js");
|
|
42
42
|
var import_address_display = require("./address-display.js");
|
|
43
43
|
var import_parent_attribution_banner = require("./parent-attribution-banner.js");
|
|
44
44
|
var import_ip_type_badge = require("./ip-type-badge.js");
|
|
@@ -50,40 +50,71 @@ function AssetMediaColumn({
|
|
|
50
50
|
imgError,
|
|
51
51
|
onImageError,
|
|
52
52
|
fallback,
|
|
53
|
+
onZoom,
|
|
54
|
+
animationUrl,
|
|
55
|
+
live = false,
|
|
53
56
|
stats
|
|
54
57
|
}) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
const [ref, isVisible] = (0, import_use_intersection_active.useIntersectionActive)();
|
|
59
|
+
const showLive = live && !!animationUrl && isVisible;
|
|
60
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref, className: "w-full lg:sticky lg:top-16", children: [
|
|
61
|
+
showLive ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-full overflow-hidden rounded-3xl aspect-square", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
62
|
+
"iframe",
|
|
63
|
+
{
|
|
64
|
+
src: animationUrl,
|
|
65
|
+
title: imageAlt,
|
|
66
|
+
sandbox: "allow-scripts",
|
|
67
|
+
loading: "lazy",
|
|
68
|
+
className: "w-full h-full border-0"
|
|
69
|
+
}
|
|
70
|
+
) }) : !image || imgError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-full overflow-hidden rounded-3xl", children: fallback }) : onZoom ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
71
|
+
import_framer_motion.motion.button,
|
|
72
|
+
{
|
|
73
|
+
type: "button",
|
|
74
|
+
onClick: onZoom,
|
|
75
|
+
"aria-label": "View full image",
|
|
76
|
+
initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
|
|
77
|
+
animate: { opacity: 1, scale: 1 },
|
|
78
|
+
transition: { duration: 0.5 },
|
|
79
|
+
className: "group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none",
|
|
80
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
81
|
+
"img",
|
|
65
82
|
{
|
|
66
83
|
src: image,
|
|
67
84
|
alt: imageAlt,
|
|
68
|
-
|
|
69
|
-
height: 0,
|
|
70
|
-
sizes: "(max-width: 1024px) 100vw, 66vw",
|
|
71
|
-
className: "w-full h-auto",
|
|
85
|
+
crossOrigin: "anonymous",
|
|
72
86
|
onError: onImageError,
|
|
87
|
+
className: "w-full h-auto max-h-[80vh] object-contain\n transition duration-300 group-hover:opacity-95 group-active:scale-[0.99]"
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
92
|
+
import_framer_motion.motion.div,
|
|
93
|
+
{
|
|
94
|
+
initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
|
|
95
|
+
animate: { opacity: 1, scale: 1 },
|
|
96
|
+
transition: { duration: 0.5 },
|
|
97
|
+
className: "block w-full overflow-hidden rounded-3xl",
|
|
98
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
99
|
+
"img",
|
|
100
|
+
{
|
|
101
|
+
src: image,
|
|
102
|
+
alt: imageAlt,
|
|
73
103
|
crossOrigin: "anonymous",
|
|
74
|
-
|
|
104
|
+
onError: onImageError,
|
|
105
|
+
className: "w-full h-auto max-h-[80vh] object-contain"
|
|
75
106
|
}
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
);
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
stats && stats.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `grid gap-3 mt-4 ${stats.length === 2 ? "grid-cols-2" : "grid-cols-1"}`, children: stats.map((stat) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-xl border border-border bg-muted/20 p-4 text-center", children: [
|
|
111
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xl font-black", children: stat.value }),
|
|
112
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1", children: [
|
|
113
|
+
stat.icon,
|
|
114
|
+
stat.label
|
|
115
|
+
] })
|
|
116
|
+
] }, stat.label)) }) : null
|
|
117
|
+
] });
|
|
87
118
|
}
|
|
88
119
|
function AssetHeaderBlock({
|
|
89
120
|
name,
|