@medialane/ui 0.126.0 → 0.126.2
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 +47 -27
- package/dist/components/animated-token-media.cjs.map +1 -1
- package/dist/components/animated-token-media.js +49 -29
- package/dist/components/animated-token-media.js.map +1 -1
- package/dist/components/asset-top-sections.cjs +53 -24
- package/dist/components/asset-top-sections.cjs.map +1 -1
- package/dist/components/asset-top-sections.js +53 -24
- package/dist/components/asset-top-sections.js.map +1 -1
- package/package.json +1 -1
|
@@ -36,6 +36,7 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
36
36
|
var import_react = require("react");
|
|
37
37
|
var import_image = __toESM(require("next/image"), 1);
|
|
38
38
|
var import_use_intersection_active = require("../utils/use-intersection-active.js");
|
|
39
|
+
var import_cn = require("../utils/cn.js");
|
|
39
40
|
function AnimatedTokenMedia({
|
|
40
41
|
image,
|
|
41
42
|
alt,
|
|
@@ -55,9 +56,11 @@ function AnimatedTokenMedia({
|
|
|
55
56
|
setInternalError(true);
|
|
56
57
|
onImageError?.();
|
|
57
58
|
};
|
|
59
|
+
const [loaded, setLoaded] = (0, import_react.useState)(false);
|
|
60
|
+
(0, import_react.useEffect)(() => setLoaded(false), [image]);
|
|
58
61
|
const [ref, isVisible] = (0, import_use_intersection_active.useIntersectionActive)();
|
|
59
62
|
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)(
|
|
63
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, className: mode === "fill" ? "absolute inset-0" : "relative w-full", children: showLive ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
61
64
|
"iframe",
|
|
62
65
|
{
|
|
63
66
|
src: animationUrl,
|
|
@@ -66,32 +69,49 @@ function AnimatedTokenMedia({
|
|
|
66
69
|
loading: "lazy",
|
|
67
70
|
className: mode === "fill" ? "absolute inset-0 w-full h-full border-0" : "w-full aspect-square border-0"
|
|
68
71
|
}
|
|
69
|
-
) : image && !imgError ?
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
72
|
+
) : image && !imgError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
73
|
+
!loaded && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
74
|
+
"div",
|
|
75
|
+
{
|
|
76
|
+
className: (0, import_cn.cn)(
|
|
77
|
+
"animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06] absolute",
|
|
78
|
+
mode === "fill" ? "inset-0" : "inset-x-0 top-0 aspect-square"
|
|
79
|
+
),
|
|
80
|
+
style: {
|
|
81
|
+
backgroundImage: "linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)",
|
|
82
|
+
backgroundSize: "200% 100%"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
mode === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
87
|
+
import_image.default,
|
|
88
|
+
{
|
|
89
|
+
src: image,
|
|
90
|
+
alt,
|
|
91
|
+
fill: true,
|
|
92
|
+
unoptimized: true,
|
|
93
|
+
sizes,
|
|
94
|
+
priority,
|
|
95
|
+
className: (0, import_cn.cn)(className, "transition-opacity duration-300", loaded ? "opacity-100" : "opacity-0"),
|
|
96
|
+
onLoad: () => setLoaded(true),
|
|
97
|
+
onError: handleImageError
|
|
98
|
+
}
|
|
99
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
100
|
+
import_image.default,
|
|
101
|
+
{
|
|
102
|
+
src: image,
|
|
103
|
+
alt,
|
|
104
|
+
width: 0,
|
|
105
|
+
height: 0,
|
|
106
|
+
sizes,
|
|
107
|
+
priority,
|
|
108
|
+
crossOrigin: "anonymous",
|
|
109
|
+
className: (0, import_cn.cn)(className ?? "w-full h-auto", "transition-opacity duration-300", loaded ? "opacity-100" : "opacity-0"),
|
|
110
|
+
onLoad: () => setLoaded(true),
|
|
111
|
+
onError: handleImageError
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] }) : fallback ?? null });
|
|
95
115
|
}
|
|
96
116
|
// Annotate the CommonJS export names for ESM import in node:
|
|
97
117
|
0 && (module.exports = {
|
|
@@ -1 +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\n image: string | null;\n alt: string;\n\n animationUrl?: string | null;\n\n live?: boolean;\n\n mode?: \"fill\" | \"natural\";\n className?: string;\n sizes?: string;\n priority?: boolean;\n\n imgError?: boolean;\n onImageError?: () => void;\n\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
|
|
1
|
+
{"version":3,"sources":["../../src/components/animated-token-media.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Image from \"next/image\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface AnimatedTokenMediaProps {\n\n image: string | null;\n alt: string;\n\n animationUrl?: string | null;\n\n live?: boolean;\n\n mode?: \"fill\" | \"natural\";\n className?: string;\n sizes?: string;\n priority?: boolean;\n\n imgError?: boolean;\n onImageError?: () => void;\n\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 [loaded, setLoaded] = useState(false);\n useEffect(() => setLoaded(false), [image]);\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\" : \"relative 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 <>\n {!loaded && (\n <div\n className={cn(\n \"animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06] absolute\",\n mode === \"fill\" ? \"inset-0\" : \"inset-x-0 top-0 aspect-square\"\n )}\n style={{\n backgroundImage:\n \"linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)\",\n backgroundSize: \"200% 100%\",\n }}\n />\n )}\n {mode === \"fill\" ? (\n <Image\n src={image}\n alt={alt}\n fill\n unoptimized\n sizes={sizes}\n priority={priority}\n className={cn(className, \"transition-opacity duration-300\", loaded ? \"opacity-100\" : \"opacity-0\")}\n onLoad={() => setLoaded(true)}\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={cn(className ?? \"w-full h-auto\", \"transition-opacity duration-300\", loaded ? \"opacity-100\" : \"opacity-0\")}\n onLoad={() => setLoaded(true)}\n onError={handleImageError}\n />\n )}\n </>\n ) : (\n fallback ?? null\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwDQ;AAtDR,mBAAoC;AACpC,mBAAkB;AAClB,qCAAsC;AACtC,gBAAmB;AAsBZ,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,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAC1C,8BAAU,MAAM,UAAU,KAAK,GAAG,CAAC,KAAK,CAAC;AAEzC,QAAM,CAAC,KAAK,SAAS,QAAI,sDAAsC;AAC/D,QAAM,WAAW,QAAQ,CAAC,CAAC,gBAAgB;AAE3C,SACE,4CAAC,SAAI,KAAU,WAAW,SAAS,SAAS,qBAAqB,mBAC9D,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,4EACG;AAAA,KAAC,UACA;AAAA,MAAC;AAAA;AAAA,QACC,eAAW;AAAA,UACT;AAAA,UACA,SAAS,SAAS,YAAY;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,UACL,iBACE;AAAA,UACF,gBAAgB;AAAA,QAClB;AAAA;AAAA,IACF;AAAA,IAED,SAAS,SACR;AAAA,MAAC,aAAAA;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL;AAAA,QACA,MAAI;AAAA,QACJ,aAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,eAAW,cAAG,WAAW,mCAAmC,SAAS,gBAAgB,WAAW;AAAA,QAChG,QAAQ,MAAM,UAAU,IAAI;AAAA,QAC5B,SAAS;AAAA;AAAA,IACX,IAEA;AAAA,MAAC,aAAAA;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,aAAY;AAAA,QACZ,eAAW,cAAG,aAAa,iBAAiB,mCAAmC,SAAS,gBAAgB,WAAW;AAAA,QACnH,QAAQ,MAAM,UAAU,IAAI;AAAA,QAC5B,SAAS;AAAA;AAAA,IACX;AAAA,KAEJ,IAEA,YAAY,MAEhB;AAEJ;","names":["Image"]}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
4
|
import Image from "next/image";
|
|
5
5
|
import { useIntersectionActive } from "../utils/use-intersection-active.js";
|
|
6
|
+
import { cn } from "../utils/cn.js";
|
|
6
7
|
function AnimatedTokenMedia({
|
|
7
8
|
image,
|
|
8
9
|
alt,
|
|
@@ -22,9 +23,11 @@ function AnimatedTokenMedia({
|
|
|
22
23
|
setInternalError(true);
|
|
23
24
|
onImageError?.();
|
|
24
25
|
};
|
|
26
|
+
const [loaded, setLoaded] = useState(false);
|
|
27
|
+
useEffect(() => setLoaded(false), [image]);
|
|
25
28
|
const [ref, isVisible] = useIntersectionActive();
|
|
26
29
|
const showLive = live && !!animationUrl && isVisible;
|
|
27
|
-
return /* @__PURE__ */ jsx("div", { ref, className: mode === "fill" ? "absolute inset-0" : "w-full", children: showLive ? /* @__PURE__ */ jsx(
|
|
30
|
+
return /* @__PURE__ */ jsx("div", { ref, className: mode === "fill" ? "absolute inset-0" : "relative w-full", children: showLive ? /* @__PURE__ */ jsx(
|
|
28
31
|
"iframe",
|
|
29
32
|
{
|
|
30
33
|
src: animationUrl,
|
|
@@ -33,32 +36,49 @@ function AnimatedTokenMedia({
|
|
|
33
36
|
loading: "lazy",
|
|
34
37
|
className: mode === "fill" ? "absolute inset-0 w-full h-full border-0" : "w-full aspect-square border-0"
|
|
35
38
|
}
|
|
36
|
-
) : image && !imgError ?
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
39
|
+
) : image && !imgError ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
40
|
+
!loaded && /* @__PURE__ */ jsx(
|
|
41
|
+
"div",
|
|
42
|
+
{
|
|
43
|
+
className: cn(
|
|
44
|
+
"animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06] absolute",
|
|
45
|
+
mode === "fill" ? "inset-0" : "inset-x-0 top-0 aspect-square"
|
|
46
|
+
),
|
|
47
|
+
style: {
|
|
48
|
+
backgroundImage: "linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)",
|
|
49
|
+
backgroundSize: "200% 100%"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
mode === "fill" ? /* @__PURE__ */ jsx(
|
|
54
|
+
Image,
|
|
55
|
+
{
|
|
56
|
+
src: image,
|
|
57
|
+
alt,
|
|
58
|
+
fill: true,
|
|
59
|
+
unoptimized: true,
|
|
60
|
+
sizes,
|
|
61
|
+
priority,
|
|
62
|
+
className: cn(className, "transition-opacity duration-300", loaded ? "opacity-100" : "opacity-0"),
|
|
63
|
+
onLoad: () => setLoaded(true),
|
|
64
|
+
onError: handleImageError
|
|
65
|
+
}
|
|
66
|
+
) : /* @__PURE__ */ jsx(
|
|
67
|
+
Image,
|
|
68
|
+
{
|
|
69
|
+
src: image,
|
|
70
|
+
alt,
|
|
71
|
+
width: 0,
|
|
72
|
+
height: 0,
|
|
73
|
+
sizes,
|
|
74
|
+
priority,
|
|
75
|
+
crossOrigin: "anonymous",
|
|
76
|
+
className: cn(className ?? "w-full h-auto", "transition-opacity duration-300", loaded ? "opacity-100" : "opacity-0"),
|
|
77
|
+
onLoad: () => setLoaded(true),
|
|
78
|
+
onError: handleImageError
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }) : fallback ?? null });
|
|
62
82
|
}
|
|
63
83
|
export {
|
|
64
84
|
AnimatedTokenMedia
|
|
@@ -1 +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\n image: string | null;\n alt: string;\n\n animationUrl?: string | null;\n\n live?: boolean;\n\n mode?: \"fill\" | \"natural\";\n className?: string;\n sizes?: string;\n priority?: boolean;\n\n imgError?: boolean;\n onImageError?: () => void;\n\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
|
|
1
|
+
{"version":3,"sources":["../../src/components/animated-token-media.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Image from \"next/image\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface AnimatedTokenMediaProps {\n\n image: string | null;\n alt: string;\n\n animationUrl?: string | null;\n\n live?: boolean;\n\n mode?: \"fill\" | \"natural\";\n className?: string;\n sizes?: string;\n priority?: boolean;\n\n imgError?: boolean;\n onImageError?: () => void;\n\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 [loaded, setLoaded] = useState(false);\n useEffect(() => setLoaded(false), [image]);\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\" : \"relative 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 <>\n {!loaded && (\n <div\n className={cn(\n \"animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06] absolute\",\n mode === \"fill\" ? \"inset-0\" : \"inset-x-0 top-0 aspect-square\"\n )}\n style={{\n backgroundImage:\n \"linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)\",\n backgroundSize: \"200% 100%\",\n }}\n />\n )}\n {mode === \"fill\" ? (\n <Image\n src={image}\n alt={alt}\n fill\n unoptimized\n sizes={sizes}\n priority={priority}\n className={cn(className, \"transition-opacity duration-300\", loaded ? \"opacity-100\" : \"opacity-0\")}\n onLoad={() => setLoaded(true)}\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={cn(className ?? \"w-full h-auto\", \"transition-opacity duration-300\", loaded ? \"opacity-100\" : \"opacity-0\")}\n onLoad={() => setLoaded(true)}\n onError={handleImageError}\n />\n )}\n </>\n ) : (\n fallback ?? null\n )}\n </div>\n );\n}\n"],"mappings":";AAwDQ,SAYA,UAZA,KAYA,YAZA;AAtDR,SAAS,WAAW,gBAAgB;AACpC,OAAO,WAAW;AAClB,SAAS,6BAA6B;AACtC,SAAS,UAAU;AAsBZ,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,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,YAAU,MAAM,UAAU,KAAK,GAAG,CAAC,KAAK,CAAC;AAEzC,QAAM,CAAC,KAAK,SAAS,IAAI,sBAAsC;AAC/D,QAAM,WAAW,QAAQ,CAAC,CAAC,gBAAgB;AAE3C,SACE,oBAAC,SAAI,KAAU,WAAW,SAAS,SAAS,qBAAqB,mBAC9D,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,iCACG;AAAA,KAAC,UACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,SAAS,SAAS,YAAY;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,UACL,iBACE;AAAA,UACF,gBAAgB;AAAA,QAClB;AAAA;AAAA,IACF;AAAA,IAED,SAAS,SACR;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL;AAAA,QACA,MAAI;AAAA,QACJ,aAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,WAAW,GAAG,WAAW,mCAAmC,SAAS,gBAAgB,WAAW;AAAA,QAChG,QAAQ,MAAM,UAAU,IAAI;AAAA,QAC5B,SAAS;AAAA;AAAA,IACX,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,aAAY;AAAA,QACZ,WAAW,GAAG,aAAa,iBAAiB,mCAAmC,SAAS,gBAAgB,WAAW;AAAA,QACnH,QAAQ,MAAM,UAAU,IAAI;AAAA,QAC5B,SAAS;AAAA;AAAA,IACX;AAAA,KAEJ,IAEA,YAAY,MAEhB;AAEJ;","names":[]}
|
|
@@ -37,8 +37,10 @@ __export(asset_top_sections_exports, {
|
|
|
37
37
|
module.exports = __toCommonJS(asset_top_sections_exports);
|
|
38
38
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
39
|
var import_link = __toESM(require("next/link"), 1);
|
|
40
|
+
var import_react = require("react");
|
|
40
41
|
var import_framer_motion = require("framer-motion");
|
|
41
42
|
var import_use_intersection_active = require("../utils/use-intersection-active.js");
|
|
43
|
+
var import_cn = require("../utils/cn.js");
|
|
42
44
|
var import_address_display = require("./address-display.js");
|
|
43
45
|
var import_parent_attribution_banner = require("./parent-attribution-banner.js");
|
|
44
46
|
var import_ip_type_badge = require("./ip-type-badge.js");
|
|
@@ -56,7 +58,19 @@ function AssetMediaColumn({
|
|
|
56
58
|
stats
|
|
57
59
|
}) {
|
|
58
60
|
const [ref, isVisible] = (0, import_use_intersection_active.useIntersectionActive)();
|
|
61
|
+
const [loaded, setLoaded] = (0, import_react.useState)(false);
|
|
62
|
+
(0, import_react.useEffect)(() => setLoaded(false), [image]);
|
|
59
63
|
const showLive = live && !!animationUrl && isVisible;
|
|
64
|
+
const shimmer = !loaded && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
65
|
+
"div",
|
|
66
|
+
{
|
|
67
|
+
className: "absolute inset-0 min-h-80 animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06]",
|
|
68
|
+
style: {
|
|
69
|
+
backgroundImage: "linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)",
|
|
70
|
+
backgroundSize: "200% 100%"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
60
74
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref, className: "w-full lg:sticky lg:top-16", children: [
|
|
61
75
|
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
76
|
"iframe",
|
|
@@ -67,7 +81,7 @@ function AssetMediaColumn({
|
|
|
67
81
|
loading: "lazy",
|
|
68
82
|
className: "w-full h-full border-0"
|
|
69
83
|
}
|
|
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.
|
|
84
|
+
) }) : !image || imgError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-full overflow-hidden rounded-3xl", children: fallback }) : onZoom ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
71
85
|
import_framer_motion.motion.button,
|
|
72
86
|
{
|
|
73
87
|
type: "button",
|
|
@@ -76,35 +90,50 @@ function AssetMediaColumn({
|
|
|
76
90
|
initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
|
|
77
91
|
animate: { opacity: 1, scale: 1 },
|
|
78
92
|
transition: { duration: 0.5 },
|
|
79
|
-
className: "group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none",
|
|
80
|
-
children:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
className: "group relative block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none",
|
|
94
|
+
children: [
|
|
95
|
+
shimmer,
|
|
96
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
97
|
+
"img",
|
|
98
|
+
{
|
|
99
|
+
src: image,
|
|
100
|
+
alt: imageAlt,
|
|
101
|
+
crossOrigin: "anonymous",
|
|
102
|
+
onLoad: () => setLoaded(true),
|
|
103
|
+
onError: onImageError,
|
|
104
|
+
className: (0, import_cn.cn)(
|
|
105
|
+
"w-full h-auto max-h-[80vh] object-contain",
|
|
106
|
+
"transition-opacity duration-300 group-hover:opacity-95 group-active:scale-[0.99]",
|
|
107
|
+
loaded ? "opacity-100" : "opacity-0"
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
]
|
|
90
112
|
}
|
|
91
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.
|
|
113
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
92
114
|
import_framer_motion.motion.div,
|
|
93
115
|
{
|
|
94
116
|
initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
|
|
95
117
|
animate: { opacity: 1, scale: 1 },
|
|
96
118
|
transition: { duration: 0.5 },
|
|
97
|
-
className: "block w-full overflow-hidden rounded-3xl",
|
|
98
|
-
children:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
className: "relative block w-full overflow-hidden rounded-3xl",
|
|
120
|
+
children: [
|
|
121
|
+
shimmer,
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
123
|
+
"img",
|
|
124
|
+
{
|
|
125
|
+
src: image,
|
|
126
|
+
alt: imageAlt,
|
|
127
|
+
crossOrigin: "anonymous",
|
|
128
|
+
onLoad: () => setLoaded(true),
|
|
129
|
+
onError: onImageError,
|
|
130
|
+
className: (0, import_cn.cn)(
|
|
131
|
+
"w-full h-auto max-h-[80vh] object-contain transition-opacity duration-300",
|
|
132
|
+
loaded ? "opacity-100" : "opacity-0"
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
]
|
|
108
137
|
}
|
|
109
138
|
),
|
|
110
139
|
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: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string | null;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n\n onZoom?: () => void;\n\n animationUrl?: string | null;\n\n live?: boolean;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n onZoom,\n animationUrl,\n live = false,\n stats,\n}: AssetMediaColumnProps) {\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const showLive = live && !!animationUrl && isVisible;\n\n return (\n <div ref={ref} className=\"w-full lg:sticky lg:top-16\">\n {showLive ? (\n <div className=\"w-full overflow-hidden rounded-3xl aspect-square\">\n <iframe\n src={animationUrl!}\n title={imageAlt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className=\"w-full h-full border-0\"\n />\n </div>\n ) : !image || imgError ? (\n <div className=\"w-full overflow-hidden rounded-3xl\">{fallback}</div>\n ) : onZoom ? (\n <motion.button\n type=\"button\"\n onClick={onZoom}\n aria-label=\"View full image\"\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none\"\n >\n\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onError={onImageError}\n className=\"w-full h-auto max-h-[80vh] object-contain\n transition duration-300 group-hover:opacity-95 group-active:scale-[0.99]\"\n />\n </motion.button>\n ) : (\n <motion.div\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"block w-full overflow-hidden rounded-3xl\"\n >\n\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onError={onImageError}\n className=\"w-full h-auto max-h-[80vh] object-contain\"\n />\n </motion.div>\n )}\n\n {stats && stats.length > 0 ? (\n <div className={`grid gap-3 mt-4 ${stats.length === 2 ? \"grid-cols-2\" : \"grid-cols-1\"}`}>\n {stats.map((stat) => (\n <div key={stat.label} className=\"rounded-xl border border-border bg-muted/20 p-4 text-center\">\n <p className=\"text-2xl font-black\">{stat.value}</p>\n <div className=\"flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1\">\n {stat.icon}\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface AssetHeaderBlockProps {\n name: string;\n description?: string | null;\n ipType?: string | null;\n showMultiEditionBadge?: boolean;\n parentContract?: string | null;\n parentTokenId?: string | null;\n}\n\nexport function AssetHeaderBlock({\n name,\n description,\n ipType,\n showMultiEditionBadge = false,\n parentContract,\n parentTokenId,\n}: AssetHeaderBlockProps) {\n return (\n <div className=\"min-w-0 flex-1\">\n {ipType || showMultiEditionBadge ? (\n <div className=\"flex items-center gap-2 flex-wrap mb-2\">\n {ipType ? <IpTypeBadge ipType={ipType} size=\"md\" /> : null}\n {showMultiEditionBadge ? (\n <span className=\"inline-flex items-center gap-1 text-2xs font-semibold px-2.5 py-1 rounded-full border border-violet-500/30 bg-violet-500/10 text-violet-500\">\n <Layers className=\"h-3 w-3\" />\n Multi-edition\n </span>\n ) : null}\n </div>\n ) : null}\n {parentContract && parentTokenId ? (\n <div className=\"mb-3\">\n <ParentAttributionBanner\n parentContract={parentContract}\n parentTokenId={parentTokenId}\n parentName={`Token #${parentTokenId}`}\n />\n </div>\n ) : null}\n <h1 className=\"text-2xl lg:text-4xl font-bold break-words\">{name}</h1>\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-1\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nexport interface AssetOwnerRowProps {\n ownerAddress: string;\n ownerHref: string;\n}\n\nexport function AssetOwnerRow({ ownerAddress, ownerHref }: AssetOwnerRowProps) {\n return (\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Owner</span>\n <Link href={ownerHref} className=\"hover:text-primary transition-colors font-medium\">\n <AddressDisplay address={ownerAddress} />\n </Link>\n </div>\n );\n}\n\nexport function buildEditionStats(totalEditions: number, uniqueOwners: number) {\n return [\n {\n value: totalEditions.toLocaleString(),\n label: \"editions minted\",\n icon: <Layers className=\"h-3 w-3\" />,\n },\n {\n value: uniqueOwners.toLocaleString(),\n label: \"unique owners\",\n icon: <Users className=\"h-3 w-3\" />,\n },\n ];\n}\n"],"mappings":"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","names":["Link"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { useEffect, useState } from \"react\";\nimport { motion } from \"framer-motion\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string | null;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n\n onZoom?: () => void;\n\n animationUrl?: string | null;\n\n live?: boolean;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n onZoom,\n animationUrl,\n live = false,\n stats,\n}: AssetMediaColumnProps) {\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const [loaded, setLoaded] = useState(false);\n useEffect(() => setLoaded(false), [image]);\n const showLive = live && !!animationUrl && isVisible;\n\n const shimmer = !loaded && (\n <div\n className=\"absolute inset-0 min-h-80 animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06]\"\n style={{\n backgroundImage:\n \"linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)\",\n backgroundSize: \"200% 100%\",\n }}\n />\n );\n\n return (\n <div ref={ref} className=\"w-full lg:sticky lg:top-16\">\n {showLive ? (\n <div className=\"w-full overflow-hidden rounded-3xl aspect-square\">\n <iframe\n src={animationUrl!}\n title={imageAlt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className=\"w-full h-full border-0\"\n />\n </div>\n ) : !image || imgError ? (\n <div className=\"w-full overflow-hidden rounded-3xl\">{fallback}</div>\n ) : onZoom ? (\n <motion.button\n type=\"button\"\n onClick={onZoom}\n aria-label=\"View full image\"\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"group relative block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none\"\n >\n {shimmer}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onLoad={() => setLoaded(true)}\n onError={onImageError}\n className={cn(\n \"w-full h-auto max-h-[80vh] object-contain\",\n \"transition-opacity duration-300 group-hover:opacity-95 group-active:scale-[0.99]\",\n loaded ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n </motion.button>\n ) : (\n <motion.div\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"relative block w-full overflow-hidden rounded-3xl\"\n >\n {shimmer}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onLoad={() => setLoaded(true)}\n onError={onImageError}\n className={cn(\n \"w-full h-auto max-h-[80vh] object-contain transition-opacity duration-300\",\n loaded ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n </motion.div>\n )}\n\n {stats && stats.length > 0 ? (\n <div className={`grid gap-3 mt-4 ${stats.length === 2 ? \"grid-cols-2\" : \"grid-cols-1\"}`}>\n {stats.map((stat) => (\n <div key={stat.label} className=\"rounded-xl border border-border bg-muted/20 p-4 text-center\">\n <p className=\"text-2xl font-black\">{stat.value}</p>\n <div className=\"flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1\">\n {stat.icon}\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface AssetHeaderBlockProps {\n name: string;\n description?: string | null;\n ipType?: string | null;\n showMultiEditionBadge?: boolean;\n parentContract?: string | null;\n parentTokenId?: string | null;\n}\n\nexport function AssetHeaderBlock({\n name,\n description,\n ipType,\n showMultiEditionBadge = false,\n parentContract,\n parentTokenId,\n}: AssetHeaderBlockProps) {\n return (\n <div className=\"min-w-0 flex-1\">\n {ipType || showMultiEditionBadge ? (\n <div className=\"flex items-center gap-2 flex-wrap mb-2\">\n {ipType ? <IpTypeBadge ipType={ipType} size=\"md\" /> : null}\n {showMultiEditionBadge ? (\n <span className=\"inline-flex items-center gap-1 text-2xs font-semibold px-2.5 py-1 rounded-full border border-violet-500/30 bg-violet-500/10 text-violet-500\">\n <Layers className=\"h-3 w-3\" />\n Multi-edition\n </span>\n ) : null}\n </div>\n ) : null}\n {parentContract && parentTokenId ? (\n <div className=\"mb-3\">\n <ParentAttributionBanner\n parentContract={parentContract}\n parentTokenId={parentTokenId}\n parentName={`Token #${parentTokenId}`}\n />\n </div>\n ) : null}\n <h1 className=\"text-2xl lg:text-4xl font-bold break-words\">{name}</h1>\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-1\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nexport interface AssetOwnerRowProps {\n ownerAddress: string;\n ownerHref: string;\n}\n\nexport function AssetOwnerRow({ ownerAddress, ownerHref }: AssetOwnerRowProps) {\n return (\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Owner</span>\n <Link href={ownerHref} className=\"hover:text-primary transition-colors font-medium\">\n <AddressDisplay address={ownerAddress} />\n </Link>\n </div>\n );\n}\n\nexport function buildEditionStats(totalEditions: number, uniqueOwners: number) {\n return [\n {\n value: totalEditions.toLocaleString(),\n label: \"editions minted\",\n icon: <Layers className=\"h-3 w-3\" />,\n },\n {\n value: uniqueOwners.toLocaleString(),\n label: \"unique owners\",\n icon: <Users className=\"h-3 w-3\" />,\n },\n ];\n}\n"],"mappings":"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","names":["Link"]}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import Link from "next/link";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
4
5
|
import { motion } from "framer-motion";
|
|
5
6
|
import { useIntersectionActive } from "../utils/use-intersection-active.js";
|
|
7
|
+
import { cn } from "../utils/cn.js";
|
|
6
8
|
import { AddressDisplay } from "./address-display.js";
|
|
7
9
|
import { ParentAttributionBanner } from "./parent-attribution-banner.js";
|
|
8
10
|
import { IpTypeBadge } from "./ip-type-badge.js";
|
|
@@ -20,7 +22,19 @@ function AssetMediaColumn({
|
|
|
20
22
|
stats
|
|
21
23
|
}) {
|
|
22
24
|
const [ref, isVisible] = useIntersectionActive();
|
|
25
|
+
const [loaded, setLoaded] = useState(false);
|
|
26
|
+
useEffect(() => setLoaded(false), [image]);
|
|
23
27
|
const showLive = live && !!animationUrl && isVisible;
|
|
28
|
+
const shimmer = !loaded && /* @__PURE__ */ jsx(
|
|
29
|
+
"div",
|
|
30
|
+
{
|
|
31
|
+
className: "absolute inset-0 min-h-80 animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06]",
|
|
32
|
+
style: {
|
|
33
|
+
backgroundImage: "linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)",
|
|
34
|
+
backgroundSize: "200% 100%"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
);
|
|
24
38
|
return /* @__PURE__ */ jsxs("div", { ref, className: "w-full lg:sticky lg:top-16", children: [
|
|
25
39
|
showLive ? /* @__PURE__ */ jsx("div", { className: "w-full overflow-hidden rounded-3xl aspect-square", children: /* @__PURE__ */ jsx(
|
|
26
40
|
"iframe",
|
|
@@ -31,7 +45,7 @@ function AssetMediaColumn({
|
|
|
31
45
|
loading: "lazy",
|
|
32
46
|
className: "w-full h-full border-0"
|
|
33
47
|
}
|
|
34
|
-
) }) : !image || imgError ? /* @__PURE__ */ jsx("div", { className: "w-full overflow-hidden rounded-3xl", children: fallback }) : onZoom ? /* @__PURE__ */
|
|
48
|
+
) }) : !image || imgError ? /* @__PURE__ */ jsx("div", { className: "w-full overflow-hidden rounded-3xl", children: fallback }) : onZoom ? /* @__PURE__ */ jsxs(
|
|
35
49
|
motion.button,
|
|
36
50
|
{
|
|
37
51
|
type: "button",
|
|
@@ -40,35 +54,50 @@ function AssetMediaColumn({
|
|
|
40
54
|
initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
|
|
41
55
|
animate: { opacity: 1, scale: 1 },
|
|
42
56
|
transition: { duration: 0.5 },
|
|
43
|
-
className: "group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none",
|
|
44
|
-
children:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
className: "group relative block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none",
|
|
58
|
+
children: [
|
|
59
|
+
shimmer,
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
"img",
|
|
62
|
+
{
|
|
63
|
+
src: image,
|
|
64
|
+
alt: imageAlt,
|
|
65
|
+
crossOrigin: "anonymous",
|
|
66
|
+
onLoad: () => setLoaded(true),
|
|
67
|
+
onError: onImageError,
|
|
68
|
+
className: cn(
|
|
69
|
+
"w-full h-auto max-h-[80vh] object-contain",
|
|
70
|
+
"transition-opacity duration-300 group-hover:opacity-95 group-active:scale-[0.99]",
|
|
71
|
+
loaded ? "opacity-100" : "opacity-0"
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
]
|
|
54
76
|
}
|
|
55
|
-
) : /* @__PURE__ */
|
|
77
|
+
) : /* @__PURE__ */ jsxs(
|
|
56
78
|
motion.div,
|
|
57
79
|
{
|
|
58
80
|
initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
|
|
59
81
|
animate: { opacity: 1, scale: 1 },
|
|
60
82
|
transition: { duration: 0.5 },
|
|
61
|
-
className: "block w-full overflow-hidden rounded-3xl",
|
|
62
|
-
children:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
className: "relative block w-full overflow-hidden rounded-3xl",
|
|
84
|
+
children: [
|
|
85
|
+
shimmer,
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
"img",
|
|
88
|
+
{
|
|
89
|
+
src: image,
|
|
90
|
+
alt: imageAlt,
|
|
91
|
+
crossOrigin: "anonymous",
|
|
92
|
+
onLoad: () => setLoaded(true),
|
|
93
|
+
onError: onImageError,
|
|
94
|
+
className: cn(
|
|
95
|
+
"w-full h-auto max-h-[80vh] object-contain transition-opacity duration-300",
|
|
96
|
+
loaded ? "opacity-100" : "opacity-0"
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
]
|
|
72
101
|
}
|
|
73
102
|
),
|
|
74
103
|
stats && stats.length > 0 ? /* @__PURE__ */ jsx("div", { className: `grid gap-3 mt-4 ${stats.length === 2 ? "grid-cols-2" : "grid-cols-1"}`, children: stats.map((stat) => /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border bg-muted/20 p-4 text-center", children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string | null;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n\n onZoom?: () => void;\n\n animationUrl?: string | null;\n\n live?: boolean;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n onZoom,\n animationUrl,\n live = false,\n stats,\n}: AssetMediaColumnProps) {\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const showLive = live && !!animationUrl && isVisible;\n\n return (\n <div ref={ref} className=\"w-full lg:sticky lg:top-16\">\n {showLive ? (\n <div className=\"w-full overflow-hidden rounded-3xl aspect-square\">\n <iframe\n src={animationUrl!}\n title={imageAlt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className=\"w-full h-full border-0\"\n />\n </div>\n ) : !image || imgError ? (\n <div className=\"w-full overflow-hidden rounded-3xl\">{fallback}</div>\n ) : onZoom ? (\n <motion.button\n type=\"button\"\n onClick={onZoom}\n aria-label=\"View full image\"\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none\"\n >\n\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onError={onImageError}\n className
|
|
1
|
+
{"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { useEffect, useState } from \"react\";\nimport { motion } from \"framer-motion\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string | null;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n\n onZoom?: () => void;\n\n animationUrl?: string | null;\n\n live?: boolean;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n onZoom,\n animationUrl,\n live = false,\n stats,\n}: AssetMediaColumnProps) {\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const [loaded, setLoaded] = useState(false);\n useEffect(() => setLoaded(false), [image]);\n const showLive = live && !!animationUrl && isVisible;\n\n const shimmer = !loaded && (\n <div\n className=\"absolute inset-0 min-h-80 animate-[shimmer_1.6s_ease-in-out_infinite] bg-foreground/[0.06]\"\n style={{\n backgroundImage:\n \"linear-gradient(90deg, transparent, color-mix(in srgb, var(--foreground) 8%, transparent), transparent)\",\n backgroundSize: \"200% 100%\",\n }}\n />\n );\n\n return (\n <div ref={ref} className=\"w-full lg:sticky lg:top-16\">\n {showLive ? (\n <div className=\"w-full overflow-hidden rounded-3xl aspect-square\">\n <iframe\n src={animationUrl!}\n title={imageAlt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className=\"w-full h-full border-0\"\n />\n </div>\n ) : !image || imgError ? (\n <div className=\"w-full overflow-hidden rounded-3xl\">{fallback}</div>\n ) : onZoom ? (\n <motion.button\n type=\"button\"\n onClick={onZoom}\n aria-label=\"View full image\"\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"group relative block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none\"\n >\n {shimmer}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onLoad={() => setLoaded(true)}\n onError={onImageError}\n className={cn(\n \"w-full h-auto max-h-[80vh] object-contain\",\n \"transition-opacity duration-300 group-hover:opacity-95 group-active:scale-[0.99]\",\n loaded ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n </motion.button>\n ) : (\n <motion.div\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"relative block w-full overflow-hidden rounded-3xl\"\n >\n {shimmer}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onLoad={() => setLoaded(true)}\n onError={onImageError}\n className={cn(\n \"w-full h-auto max-h-[80vh] object-contain transition-opacity duration-300\",\n loaded ? \"opacity-100\" : \"opacity-0\"\n )}\n />\n </motion.div>\n )}\n\n {stats && stats.length > 0 ? (\n <div className={`grid gap-3 mt-4 ${stats.length === 2 ? \"grid-cols-2\" : \"grid-cols-1\"}`}>\n {stats.map((stat) => (\n <div key={stat.label} className=\"rounded-xl border border-border bg-muted/20 p-4 text-center\">\n <p className=\"text-2xl font-black\">{stat.value}</p>\n <div className=\"flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1\">\n {stat.icon}\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface AssetHeaderBlockProps {\n name: string;\n description?: string | null;\n ipType?: string | null;\n showMultiEditionBadge?: boolean;\n parentContract?: string | null;\n parentTokenId?: string | null;\n}\n\nexport function AssetHeaderBlock({\n name,\n description,\n ipType,\n showMultiEditionBadge = false,\n parentContract,\n parentTokenId,\n}: AssetHeaderBlockProps) {\n return (\n <div className=\"min-w-0 flex-1\">\n {ipType || showMultiEditionBadge ? (\n <div className=\"flex items-center gap-2 flex-wrap mb-2\">\n {ipType ? <IpTypeBadge ipType={ipType} size=\"md\" /> : null}\n {showMultiEditionBadge ? (\n <span className=\"inline-flex items-center gap-1 text-2xs font-semibold px-2.5 py-1 rounded-full border border-violet-500/30 bg-violet-500/10 text-violet-500\">\n <Layers className=\"h-3 w-3\" />\n Multi-edition\n </span>\n ) : null}\n </div>\n ) : null}\n {parentContract && parentTokenId ? (\n <div className=\"mb-3\">\n <ParentAttributionBanner\n parentContract={parentContract}\n parentTokenId={parentTokenId}\n parentName={`Token #${parentTokenId}`}\n />\n </div>\n ) : null}\n <h1 className=\"text-2xl lg:text-4xl font-bold break-words\">{name}</h1>\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-1\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nexport interface AssetOwnerRowProps {\n ownerAddress: string;\n ownerHref: string;\n}\n\nexport function AssetOwnerRow({ ownerAddress, ownerHref }: AssetOwnerRowProps) {\n return (\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Owner</span>\n <Link href={ownerHref} className=\"hover:text-primary transition-colors font-medium\">\n <AddressDisplay address={ownerAddress} />\n </Link>\n </div>\n );\n}\n\nexport function buildEditionStats(totalEditions: number, uniqueOwners: number) {\n return [\n {\n value: totalEditions.toLocaleString(),\n label: \"editions minted\",\n icon: <Layers className=\"h-3 w-3\" />,\n },\n {\n value: uniqueOwners.toLocaleString(),\n label: \"unique owners\",\n icon: <Users className=\"h-3 w-3\" />,\n },\n ];\n}\n"],"mappings":"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","names":[]}
|