@medialane/ui 0.134.2 → 0.135.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/coin-guarantees.cjs +124 -0
- package/dist/components/coin-guarantees.cjs.map +1 -0
- package/dist/components/coin-guarantees.d.cts +23 -0
- package/dist/components/coin-guarantees.d.ts +23 -0
- package/dist/components/coin-guarantees.js +100 -0
- package/dist/components/coin-guarantees.js.map +1 -0
- package/dist/components/coin-row.cjs +12 -11
- package/dist/components/coin-row.cjs.map +1 -1
- package/dist/components/coin-row.d.cts +1 -1
- package/dist/components/coin-row.d.ts +1 -1
- package/dist/components/coin-row.js +14 -13
- package/dist/components/coin-row.js.map +1 -1
- package/dist/components/coins-explorer.cjs +1 -2
- package/dist/components/coins-explorer.cjs.map +1 -1
- package/dist/components/coins-explorer.d.cts +2 -2
- package/dist/components/coins-explorer.d.ts +2 -2
- package/dist/components/coins-explorer.js +2 -3
- package/dist/components/coins-explorer.js.map +1 -1
- package/dist/data/coins.cjs +16 -3
- package/dist/data/coins.cjs.map +1 -1
- package/dist/data/coins.d.cts +4 -2
- package/dist/data/coins.d.ts +4 -2
- package/dist/data/coins.js +14 -3
- package/dist/data/coins.js.map +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/format.cjs +4 -0
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.js +4 -0
- package/dist/utils/format.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var coin_guarantees_exports = {};
|
|
21
|
+
__export(coin_guarantees_exports, {
|
|
22
|
+
CoinGuarantees: () => CoinGuarantees
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(coin_guarantees_exports);
|
|
25
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
|
+
var import_lucide_react = require("lucide-react");
|
|
27
|
+
var import_cn = require("../utils/cn.js");
|
|
28
|
+
function whole(raw, decimals) {
|
|
29
|
+
try {
|
|
30
|
+
const units = BigInt(raw);
|
|
31
|
+
if (units <= 0n) return null;
|
|
32
|
+
const v = Number(units) / 10 ** decimals;
|
|
33
|
+
return isFinite(v) && v >= 1 ? v : null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const compact = (n) => new Intl.NumberFormat(void 0, { notation: "compact", maximumFractionDigits: 2 }).format(n);
|
|
39
|
+
function Line({
|
|
40
|
+
label,
|
|
41
|
+
value,
|
|
42
|
+
detail,
|
|
43
|
+
href
|
|
44
|
+
}) {
|
|
45
|
+
const body = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
46
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-semibold tabular-nums", children: value }),
|
|
47
|
+
detail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground", children: detail }),
|
|
48
|
+
href && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ExternalLink, { className: "h-3 w-3 text-muted-foreground/60" })
|
|
49
|
+
] });
|
|
50
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-baseline justify-between gap-3 border-b border-border/40 py-2.5 last:border-b-0", children: [
|
|
51
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
|
|
52
|
+
href ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53
|
+
"a",
|
|
54
|
+
{
|
|
55
|
+
href,
|
|
56
|
+
target: "_blank",
|
|
57
|
+
rel: "noopener noreferrer",
|
|
58
|
+
className: "inline-flex items-baseline gap-1.5 transition-colors hover:text-primary active:opacity-70",
|
|
59
|
+
children: body
|
|
60
|
+
}
|
|
61
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "inline-flex items-baseline gap-1.5", children: body })
|
|
62
|
+
] });
|
|
63
|
+
}
|
|
64
|
+
function CoinGuarantees({
|
|
65
|
+
data,
|
|
66
|
+
isLoading,
|
|
67
|
+
decimals = 18,
|
|
68
|
+
maxTeamAllocationPercent,
|
|
69
|
+
contractUrl,
|
|
70
|
+
liquidityUrl,
|
|
71
|
+
blockUrl,
|
|
72
|
+
className
|
|
73
|
+
}) {
|
|
74
|
+
if (isLoading) {
|
|
75
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_cn.cn)("space-y-2.5", className), children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-9 animate-pulse rounded bg-muted-foreground/10" }, i)) });
|
|
76
|
+
}
|
|
77
|
+
if (!data) return null;
|
|
78
|
+
const supply = whole(data.totalSupplyRaw, decimals);
|
|
79
|
+
const allocation = whole(data.teamAllocationRaw, decimals);
|
|
80
|
+
const pct = data.teamAllocationPercent;
|
|
81
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, children: [
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mb-1 text-2xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Guaranteed by the contract" }),
|
|
83
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
84
|
+
Line,
|
|
85
|
+
{
|
|
86
|
+
label: "Creator allocation",
|
|
87
|
+
value: pct != null ? `${pct}%` : allocation != null ? compact(allocation) : "\u2014",
|
|
88
|
+
detail: `capped at ${maxTeamAllocationPercent}%`,
|
|
89
|
+
href: contractUrl
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
93
|
+
Line,
|
|
94
|
+
{
|
|
95
|
+
label: "Total supply",
|
|
96
|
+
value: supply != null ? compact(supply) : "\u2014",
|
|
97
|
+
detail: "fixed, cannot be increased",
|
|
98
|
+
href: contractUrl
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
data.isLaunched ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
102
|
+
Line,
|
|
103
|
+
{
|
|
104
|
+
label: "Liquidity",
|
|
105
|
+
value: "Locked permanently",
|
|
106
|
+
detail: data.liquidityPositionId ? `position #${data.liquidityPositionId}` : void 0,
|
|
107
|
+
href: liquidityUrl
|
|
108
|
+
}
|
|
109
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, { label: "Liquidity", value: "Not launched yet" }),
|
|
110
|
+
data.launchedAtBlock != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
111
|
+
Line,
|
|
112
|
+
{
|
|
113
|
+
label: "Launched",
|
|
114
|
+
value: `Block ${data.launchedAtBlock.toLocaleString()}`,
|
|
115
|
+
href: blockUrl
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
] });
|
|
119
|
+
}
|
|
120
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
121
|
+
0 && (module.exports = {
|
|
122
|
+
CoinGuarantees
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=coin-guarantees.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-guarantees.tsx"],"sourcesContent":["\"use client\";\n\nimport { ExternalLink } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinGuaranteesData {\n isLaunched: boolean;\n launchedAtBlock: number | null;\n totalSupplyRaw: string;\n teamAllocationRaw: string;\n teamAllocationPercent: number | null;\n liquidityPositionId: string | null;\n}\n\nexport interface CoinGuaranteesProps {\n data: CoinGuaranteesData | null;\n isLoading?: boolean;\n decimals?: number;\n maxTeamAllocationPercent: number;\n\n contractUrl?: string;\n liquidityUrl?: string;\n blockUrl?: string;\n className?: string;\n}\n\nfunction whole(raw: string, decimals: number): number | null {\n try {\n const units = BigInt(raw);\n if (units <= 0n) return null;\n const v = Number(units) / 10 ** decimals;\n return isFinite(v) && v >= 1 ? v : null;\n } catch {\n return null;\n }\n}\n\nconst compact = (n: number) =>\n new Intl.NumberFormat(undefined, { notation: \"compact\", maximumFractionDigits: 2 }).format(n);\n\nfunction Line({\n label,\n value,\n detail,\n href,\n}: {\n label: string;\n value: string;\n detail?: string;\n href?: string;\n}) {\n const body = (\n <>\n <span className=\"text-sm font-semibold tabular-nums\">{value}</span>\n {detail && <span className=\"text-xs text-muted-foreground\">{detail}</span>}\n {href && <ExternalLink className=\"h-3 w-3 text-muted-foreground/60\" />}\n </>\n );\n\n return (\n <div className=\"flex items-baseline justify-between gap-3 border-b border-border/40 py-2.5 last:border-b-0\">\n <span className=\"text-xs text-muted-foreground\">{label}</span>\n {href ? (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-baseline gap-1.5 transition-colors hover:text-primary active:opacity-70\"\n >\n {body}\n </a>\n ) : (\n <span className=\"inline-flex items-baseline gap-1.5\">{body}</span>\n )}\n </div>\n );\n}\n\nexport function CoinGuarantees({\n data,\n isLoading,\n decimals = 18,\n maxTeamAllocationPercent,\n contractUrl,\n liquidityUrl,\n blockUrl,\n className,\n}: CoinGuaranteesProps) {\n if (isLoading) {\n return (\n <div className={cn(\"space-y-2.5\", className)}>\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-9 animate-pulse rounded bg-muted-foreground/10\" />\n ))}\n </div>\n );\n }\n\n if (!data) return null;\n\n const supply = whole(data.totalSupplyRaw, decimals);\n const allocation = whole(data.teamAllocationRaw, decimals);\n const pct = data.teamAllocationPercent;\n\n return (\n <div className={className}>\n <p className=\"mb-1 text-2xs font-semibold uppercase tracking-wide text-muted-foreground\">\n Guaranteed by the contract\n </p>\n\n <Line\n label=\"Creator allocation\"\n value={pct != null ? `${pct}%` : allocation != null ? compact(allocation) : \"—\"}\n detail={`capped at ${maxTeamAllocationPercent}%`}\n href={contractUrl}\n />\n\n <Line\n label=\"Total supply\"\n value={supply != null ? compact(supply) : \"—\"}\n detail=\"fixed, cannot be increased\"\n href={contractUrl}\n />\n\n {data.isLaunched ? (\n <Line\n label=\"Liquidity\"\n value=\"Locked permanently\"\n detail={data.liquidityPositionId ? `position #${data.liquidityPositionId}` : undefined}\n href={liquidityUrl}\n />\n ) : (\n <Line label=\"Liquidity\" value=\"Not launched yet\" />\n )}\n\n {data.launchedAtBlock != null && (\n <Line\n label=\"Launched\"\n value={`Block ${data.launchedAtBlock.toLocaleString()}`}\n href={blockUrl}\n />\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoDI;AAlDJ,0BAA6B;AAC7B,gBAAmB;AAuBnB,SAAS,MAAM,KAAa,UAAiC;AAC3D,MAAI;AACF,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,SAAS,GAAI,QAAO;AACxB,UAAM,IAAI,OAAO,KAAK,IAAI,MAAM;AAChC,WAAO,SAAS,CAAC,KAAK,KAAK,IAAI,IAAI;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,UAAU,CAAC,MACf,IAAI,KAAK,aAAa,QAAW,EAAE,UAAU,WAAW,uBAAuB,EAAE,CAAC,EAAE,OAAO,CAAC;AAE9F,SAAS,KAAK;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,OACJ,4EACE;AAAA,gDAAC,UAAK,WAAU,sCAAsC,iBAAM;AAAA,IAC3D,UAAU,4CAAC,UAAK,WAAU,iCAAiC,kBAAO;AAAA,IAClE,QAAQ,4CAAC,oCAAa,WAAU,oCAAmC;AAAA,KACtE;AAGF,SACE,6CAAC,SAAI,WAAU,8FACb;AAAA,gDAAC,UAAK,WAAU,iCAAiC,iBAAM;AAAA,IACtD,OACC;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,QAAO;AAAA,QACP,KAAI;AAAA,QACJ,WAAU;AAAA,QAET;AAAA;AAAA,IACH,IAEA,4CAAC,UAAK,WAAU,sCAAsC,gBAAK;AAAA,KAE/D;AAEJ;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,MAAI,WAAW;AACb,WACE,4CAAC,SAAI,eAAW,cAAG,eAAe,SAAS,GACxC,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,4CAAC,SAAY,WAAU,sDAAb,CAAgE,CAC3E,GACH;AAAA,EAEJ;AAEA,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,SAAS,MAAM,KAAK,gBAAgB,QAAQ;AAClD,QAAM,aAAa,MAAM,KAAK,mBAAmB,QAAQ;AACzD,QAAM,MAAM,KAAK;AAEjB,SACE,6CAAC,SAAI,WACH;AAAA,gDAAC,OAAE,WAAU,6EAA4E,wCAEzF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,OAAO,OAAO,GAAG,GAAG,MAAM,cAAc,OAAO,QAAQ,UAAU,IAAI;AAAA,QAC5E,QAAQ,aAAa,wBAAwB;AAAA,QAC7C,MAAM;AAAA;AAAA,IACR;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,UAAU,OAAO,QAAQ,MAAM,IAAI;AAAA,QAC1C,QAAO;AAAA,QACP,MAAM;AAAA;AAAA,IACR;AAAA,IAEC,KAAK,aACJ;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAM;AAAA,QACN,QAAQ,KAAK,sBAAsB,aAAa,KAAK,mBAAmB,KAAK;AAAA,QAC7E,MAAM;AAAA;AAAA,IACR,IAEA,4CAAC,QAAK,OAAM,aAAY,OAAM,oBAAmB;AAAA,IAGlD,KAAK,mBAAmB,QACvB;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,SAAS,KAAK,gBAAgB,eAAe,CAAC;AAAA,QACrD,MAAM;AAAA;AAAA,IACR;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface CoinGuaranteesData {
|
|
4
|
+
isLaunched: boolean;
|
|
5
|
+
launchedAtBlock: number | null;
|
|
6
|
+
totalSupplyRaw: string;
|
|
7
|
+
teamAllocationRaw: string;
|
|
8
|
+
teamAllocationPercent: number | null;
|
|
9
|
+
liquidityPositionId: string | null;
|
|
10
|
+
}
|
|
11
|
+
interface CoinGuaranteesProps {
|
|
12
|
+
data: CoinGuaranteesData | null;
|
|
13
|
+
isLoading?: boolean;
|
|
14
|
+
decimals?: number;
|
|
15
|
+
maxTeamAllocationPercent: number;
|
|
16
|
+
contractUrl?: string;
|
|
17
|
+
liquidityUrl?: string;
|
|
18
|
+
blockUrl?: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
declare function CoinGuarantees({ data, isLoading, decimals, maxTeamAllocationPercent, contractUrl, liquidityUrl, blockUrl, className, }: CoinGuaranteesProps): react_jsx_runtime.JSX.Element | null;
|
|
22
|
+
|
|
23
|
+
export { CoinGuarantees, type CoinGuaranteesData, type CoinGuaranteesProps };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface CoinGuaranteesData {
|
|
4
|
+
isLaunched: boolean;
|
|
5
|
+
launchedAtBlock: number | null;
|
|
6
|
+
totalSupplyRaw: string;
|
|
7
|
+
teamAllocationRaw: string;
|
|
8
|
+
teamAllocationPercent: number | null;
|
|
9
|
+
liquidityPositionId: string | null;
|
|
10
|
+
}
|
|
11
|
+
interface CoinGuaranteesProps {
|
|
12
|
+
data: CoinGuaranteesData | null;
|
|
13
|
+
isLoading?: boolean;
|
|
14
|
+
decimals?: number;
|
|
15
|
+
maxTeamAllocationPercent: number;
|
|
16
|
+
contractUrl?: string;
|
|
17
|
+
liquidityUrl?: string;
|
|
18
|
+
blockUrl?: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
declare function CoinGuarantees({ data, isLoading, decimals, maxTeamAllocationPercent, contractUrl, liquidityUrl, blockUrl, className, }: CoinGuaranteesProps): react_jsx_runtime.JSX.Element | null;
|
|
22
|
+
|
|
23
|
+
export { CoinGuarantees, type CoinGuaranteesData, type CoinGuaranteesProps };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ExternalLink } from "lucide-react";
|
|
4
|
+
import { cn } from "../utils/cn.js";
|
|
5
|
+
function whole(raw, decimals) {
|
|
6
|
+
try {
|
|
7
|
+
const units = BigInt(raw);
|
|
8
|
+
if (units <= 0n) return null;
|
|
9
|
+
const v = Number(units) / 10 ** decimals;
|
|
10
|
+
return isFinite(v) && v >= 1 ? v : null;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const compact = (n) => new Intl.NumberFormat(void 0, { notation: "compact", maximumFractionDigits: 2 }).format(n);
|
|
16
|
+
function Line({
|
|
17
|
+
label,
|
|
18
|
+
value,
|
|
19
|
+
detail,
|
|
20
|
+
href
|
|
21
|
+
}) {
|
|
22
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: value }),
|
|
24
|
+
detail && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: detail }),
|
|
25
|
+
href && /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3 text-muted-foreground/60" })
|
|
26
|
+
] });
|
|
27
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-3 border-b border-border/40 py-2.5 last:border-b-0", children: [
|
|
28
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: label }),
|
|
29
|
+
href ? /* @__PURE__ */ jsx(
|
|
30
|
+
"a",
|
|
31
|
+
{
|
|
32
|
+
href,
|
|
33
|
+
target: "_blank",
|
|
34
|
+
rel: "noopener noreferrer",
|
|
35
|
+
className: "inline-flex items-baseline gap-1.5 transition-colors hover:text-primary active:opacity-70",
|
|
36
|
+
children: body
|
|
37
|
+
}
|
|
38
|
+
) : /* @__PURE__ */ jsx("span", { className: "inline-flex items-baseline gap-1.5", children: body })
|
|
39
|
+
] });
|
|
40
|
+
}
|
|
41
|
+
function CoinGuarantees({
|
|
42
|
+
data,
|
|
43
|
+
isLoading,
|
|
44
|
+
decimals = 18,
|
|
45
|
+
maxTeamAllocationPercent,
|
|
46
|
+
contractUrl,
|
|
47
|
+
liquidityUrl,
|
|
48
|
+
blockUrl,
|
|
49
|
+
className
|
|
50
|
+
}) {
|
|
51
|
+
if (isLoading) {
|
|
52
|
+
return /* @__PURE__ */ jsx("div", { className: cn("space-y-2.5", className), children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-9 animate-pulse rounded bg-muted-foreground/10" }, i)) });
|
|
53
|
+
}
|
|
54
|
+
if (!data) return null;
|
|
55
|
+
const supply = whole(data.totalSupplyRaw, decimals);
|
|
56
|
+
const allocation = whole(data.teamAllocationRaw, decimals);
|
|
57
|
+
const pct = data.teamAllocationPercent;
|
|
58
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
59
|
+
/* @__PURE__ */ jsx("p", { className: "mb-1 text-2xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Guaranteed by the contract" }),
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
Line,
|
|
62
|
+
{
|
|
63
|
+
label: "Creator allocation",
|
|
64
|
+
value: pct != null ? `${pct}%` : allocation != null ? compact(allocation) : "\u2014",
|
|
65
|
+
detail: `capped at ${maxTeamAllocationPercent}%`,
|
|
66
|
+
href: contractUrl
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
Line,
|
|
71
|
+
{
|
|
72
|
+
label: "Total supply",
|
|
73
|
+
value: supply != null ? compact(supply) : "\u2014",
|
|
74
|
+
detail: "fixed, cannot be increased",
|
|
75
|
+
href: contractUrl
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
data.isLaunched ? /* @__PURE__ */ jsx(
|
|
79
|
+
Line,
|
|
80
|
+
{
|
|
81
|
+
label: "Liquidity",
|
|
82
|
+
value: "Locked permanently",
|
|
83
|
+
detail: data.liquidityPositionId ? `position #${data.liquidityPositionId}` : void 0,
|
|
84
|
+
href: liquidityUrl
|
|
85
|
+
}
|
|
86
|
+
) : /* @__PURE__ */ jsx(Line, { label: "Liquidity", value: "Not launched yet" }),
|
|
87
|
+
data.launchedAtBlock != null && /* @__PURE__ */ jsx(
|
|
88
|
+
Line,
|
|
89
|
+
{
|
|
90
|
+
label: "Launched",
|
|
91
|
+
value: `Block ${data.launchedAtBlock.toLocaleString()}`,
|
|
92
|
+
href: blockUrl
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
] });
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
CoinGuarantees
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=coin-guarantees.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-guarantees.tsx"],"sourcesContent":["\"use client\";\n\nimport { ExternalLink } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinGuaranteesData {\n isLaunched: boolean;\n launchedAtBlock: number | null;\n totalSupplyRaw: string;\n teamAllocationRaw: string;\n teamAllocationPercent: number | null;\n liquidityPositionId: string | null;\n}\n\nexport interface CoinGuaranteesProps {\n data: CoinGuaranteesData | null;\n isLoading?: boolean;\n decimals?: number;\n maxTeamAllocationPercent: number;\n\n contractUrl?: string;\n liquidityUrl?: string;\n blockUrl?: string;\n className?: string;\n}\n\nfunction whole(raw: string, decimals: number): number | null {\n try {\n const units = BigInt(raw);\n if (units <= 0n) return null;\n const v = Number(units) / 10 ** decimals;\n return isFinite(v) && v >= 1 ? v : null;\n } catch {\n return null;\n }\n}\n\nconst compact = (n: number) =>\n new Intl.NumberFormat(undefined, { notation: \"compact\", maximumFractionDigits: 2 }).format(n);\n\nfunction Line({\n label,\n value,\n detail,\n href,\n}: {\n label: string;\n value: string;\n detail?: string;\n href?: string;\n}) {\n const body = (\n <>\n <span className=\"text-sm font-semibold tabular-nums\">{value}</span>\n {detail && <span className=\"text-xs text-muted-foreground\">{detail}</span>}\n {href && <ExternalLink className=\"h-3 w-3 text-muted-foreground/60\" />}\n </>\n );\n\n return (\n <div className=\"flex items-baseline justify-between gap-3 border-b border-border/40 py-2.5 last:border-b-0\">\n <span className=\"text-xs text-muted-foreground\">{label}</span>\n {href ? (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-baseline gap-1.5 transition-colors hover:text-primary active:opacity-70\"\n >\n {body}\n </a>\n ) : (\n <span className=\"inline-flex items-baseline gap-1.5\">{body}</span>\n )}\n </div>\n );\n}\n\nexport function CoinGuarantees({\n data,\n isLoading,\n decimals = 18,\n maxTeamAllocationPercent,\n contractUrl,\n liquidityUrl,\n blockUrl,\n className,\n}: CoinGuaranteesProps) {\n if (isLoading) {\n return (\n <div className={cn(\"space-y-2.5\", className)}>\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-9 animate-pulse rounded bg-muted-foreground/10\" />\n ))}\n </div>\n );\n }\n\n if (!data) return null;\n\n const supply = whole(data.totalSupplyRaw, decimals);\n const allocation = whole(data.teamAllocationRaw, decimals);\n const pct = data.teamAllocationPercent;\n\n return (\n <div className={className}>\n <p className=\"mb-1 text-2xs font-semibold uppercase tracking-wide text-muted-foreground\">\n Guaranteed by the contract\n </p>\n\n <Line\n label=\"Creator allocation\"\n value={pct != null ? `${pct}%` : allocation != null ? compact(allocation) : \"—\"}\n detail={`capped at ${maxTeamAllocationPercent}%`}\n href={contractUrl}\n />\n\n <Line\n label=\"Total supply\"\n value={supply != null ? compact(supply) : \"—\"}\n detail=\"fixed, cannot be increased\"\n href={contractUrl}\n />\n\n {data.isLaunched ? (\n <Line\n label=\"Liquidity\"\n value=\"Locked permanently\"\n detail={data.liquidityPositionId ? `position #${data.liquidityPositionId}` : undefined}\n href={liquidityUrl}\n />\n ) : (\n <Line label=\"Liquidity\" value=\"Not launched yet\" />\n )}\n\n {data.launchedAtBlock != null && (\n <Line\n label=\"Launched\"\n value={`Block ${data.launchedAtBlock.toLocaleString()}`}\n href={blockUrl}\n />\n )}\n </div>\n );\n}\n"],"mappings":";AAoDI,mBACE,KADF;AAlDJ,SAAS,oBAAoB;AAC7B,SAAS,UAAU;AAuBnB,SAAS,MAAM,KAAa,UAAiC;AAC3D,MAAI;AACF,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,SAAS,GAAI,QAAO;AACxB,UAAM,IAAI,OAAO,KAAK,IAAI,MAAM;AAChC,WAAO,SAAS,CAAC,KAAK,KAAK,IAAI,IAAI;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,UAAU,CAAC,MACf,IAAI,KAAK,aAAa,QAAW,EAAE,UAAU,WAAW,uBAAuB,EAAE,CAAC,EAAE,OAAO,CAAC;AAE9F,SAAS,KAAK;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,OACJ,iCACE;AAAA,wBAAC,UAAK,WAAU,sCAAsC,iBAAM;AAAA,IAC3D,UAAU,oBAAC,UAAK,WAAU,iCAAiC,kBAAO;AAAA,IAClE,QAAQ,oBAAC,gBAAa,WAAU,oCAAmC;AAAA,KACtE;AAGF,SACE,qBAAC,SAAI,WAAU,8FACb;AAAA,wBAAC,UAAK,WAAU,iCAAiC,iBAAM;AAAA,IACtD,OACC;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,QAAO;AAAA,QACP,KAAI;AAAA,QACJ,WAAU;AAAA,QAET;AAAA;AAAA,IACH,IAEA,oBAAC,UAAK,WAAU,sCAAsC,gBAAK;AAAA,KAE/D;AAEJ;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,MAAI,WAAW;AACb,WACE,oBAAC,SAAI,WAAW,GAAG,eAAe,SAAS,GACxC,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,oBAAC,SAAY,WAAU,sDAAb,CAAgE,CAC3E,GACH;AAAA,EAEJ;AAEA,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,SAAS,MAAM,KAAK,gBAAgB,QAAQ;AAClD,QAAM,aAAa,MAAM,KAAK,mBAAmB,QAAQ;AACzD,QAAM,MAAM,KAAK;AAEjB,SACE,qBAAC,SAAI,WACH;AAAA,wBAAC,OAAE,WAAU,6EAA4E,wCAEzF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,OAAO,OAAO,GAAG,GAAG,MAAM,cAAc,OAAO,QAAQ,UAAU,IAAI;AAAA,QAC5E,QAAQ,aAAa,wBAAwB;AAAA,QAC7C,MAAM;AAAA;AAAA,IACR;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,UAAU,OAAO,QAAQ,MAAM,IAAI;AAAA,QAC1C,QAAO;AAAA,QACP,MAAM;AAAA;AAAA,IACR;AAAA,IAEC,KAAK,aACJ;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAM;AAAA,QACN,QAAQ,KAAK,sBAAsB,aAAa,KAAK,mBAAmB,KAAK;AAAA,QAC7E,MAAM;AAAA;AAAA,IACR,IAEA,oBAAC,QAAK,OAAM,aAAY,OAAM,oBAAmB;AAAA,IAGlD,KAAK,mBAAmB,QACvB;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,SAAS,KAAK,gBAAgB,eAAe,CAAC;AAAA,QACrD,MAAM;AAAA;AAAA,IACR;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -40,11 +40,10 @@ var import_link = __toESM(require("./link.js"), 1);
|
|
|
40
40
|
var import_image = __toESM(require("./image.js"), 1);
|
|
41
41
|
var import_cn = require("../utils/cn.js");
|
|
42
42
|
var import_ipfs = require("../utils/ipfs.js");
|
|
43
|
-
var
|
|
43
|
+
var import_dual_price = require("./dual-price.js");
|
|
44
44
|
var import_coins = require("../data/coins.js");
|
|
45
45
|
var import_format = require("../utils/format.js");
|
|
46
|
-
const COIN_GRID = "grid grid-cols-[minmax(0,1fr)
|
|
47
|
-
const kindLabel = (kind) => kind === "creator" ? "Creator Coin" : "Memecoin";
|
|
46
|
+
const COIN_GRID = "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:gap-4";
|
|
48
47
|
function CoinAvatar({ collection, size = 36 }) {
|
|
49
48
|
const logoUri = collection.profile?.image ?? collection.image;
|
|
50
49
|
const logo = logoUri ? (0, import_ipfs.ipfsToHttp)(logoUri) : null;
|
|
@@ -72,13 +71,15 @@ function PriceCell({ price, status, isLoading }) {
|
|
|
72
71
|
return status === "pre-launch" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "block text-right text-xs text-muted-foreground", children: "Not trading yet" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "block text-right text-muted-foreground", children: "\u2014" });
|
|
73
72
|
}
|
|
74
73
|
const usd = price.quoteUsdRate != null ? (0, import_format.formatUsdPrice)(price.quotePerCoin * price.quoteUsdRate) : null;
|
|
75
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
75
|
+
import_dual_price.DualPrice,
|
|
76
|
+
{
|
|
77
|
+
amountFormatted: String(price.quotePerCoin),
|
|
78
|
+
currency: price.quoteSymbol,
|
|
79
|
+
usdValue: usd,
|
|
80
|
+
scale: "card"
|
|
81
|
+
}
|
|
82
|
+
) });
|
|
82
83
|
}
|
|
83
84
|
function CoinRow({ collection, usePrice, href, showKind = false }) {
|
|
84
85
|
const { price, status, isLoading } = usePrice(collection);
|
|
@@ -100,7 +101,7 @@ function CoinRow({ collection, usePrice, href, showKind = false }) {
|
|
|
100
101
|
collection.symbol ?? "\u2014",
|
|
101
102
|
showKind && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-muted-foreground/60", children: [
|
|
102
103
|
" \xB7 ",
|
|
103
|
-
|
|
104
|
+
(0, import_coins.coinKindLabel)((0, import_coins.coinKind)(collection.service))
|
|
104
105
|
] })
|
|
105
106
|
] })
|
|
106
107
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coin-row.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-row.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { DualPrice } from \"./dual-price.js\";\nimport {\n coinKind, coinKindLabel, coinAccentToken,\n type CoinCollectionLike, type CoinPriceLike,\n} from \"../data/coins.js\";\nimport { formatUsdPrice } from \"../utils/format.js\";\n\nexport type CoinMarketStatus = \"live\" | \"pre-launch\" | \"unavailable\";\n\nexport type UseCoinPrice = (collection: CoinCollectionLike) => {\n price: CoinPriceLike | null;\n status?: CoinMarketStatus;\n isLoading: boolean;\n};\n\nexport const COIN_GRID = \"grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:gap-4\";\n\nexport interface CoinRowProps {\n collection: CoinCollectionLike;\n usePrice: UseCoinPrice;\n href: string;\n showKind?: boolean;\n}\n\nexport function CoinAvatar({ collection, size = 36 }: { collection: CoinCollectionLike; size?: number }) {\n const logoUri = collection.profile?.image ?? collection.image;\n const logo = logoUri ? ipfsToHttp(logoUri) : null;\n const initials = (collection.symbol ?? collection.name ?? \"?\").trim().slice(0, 2).toUpperCase();\n const accent = coinAccentToken(collection.symbol ?? collection.name ?? collection.contractAddress);\n\n return (\n <div\n className={cn(\"relative shrink-0 overflow-hidden rounded-full\", accent)}\n style={{ width: size, height: size }}\n >\n {logo ? (\n <Image src={logo} alt=\"\" fill sizes={`${size}px`} className=\"object-cover\" unoptimized />\n ) : (\n <span\n className=\"flex h-full w-full items-center justify-center font-bold leading-none text-white\"\n style={{ fontSize: size * 0.36 }}\n >\n {initials}\n </span>\n )}\n </div>\n );\n}\n\nfunction PriceCell({ price, status, isLoading }: { price: CoinPriceLike | null; status?: CoinMarketStatus; isLoading: boolean }) {\n if (isLoading) return <span className=\"ml-auto block h-4 w-16 animate-pulse rounded bg-muted-foreground/20\" />;\n if (!price) {\n return status === \"pre-launch\" ? (\n <span className=\"block text-right text-xs text-muted-foreground\">Not trading yet</span>\n ) : (\n <span className=\"block text-right text-muted-foreground\">—</span>\n );\n }\n\n const usd = price.quoteUsdRate != null ? formatUsdPrice(price.quotePerCoin * price.quoteUsdRate) : null;\n\n return (\n <span className=\"flex justify-end\">\n <DualPrice\n amountFormatted={String(price.quotePerCoin)}\n currency={price.quoteSymbol}\n usdValue={usd}\n scale=\"card\"\n />\n </span>\n );\n}\n\nexport function CoinRow({ collection, usePrice, href, showKind = false }: CoinRowProps) {\n const { price, status, isLoading } = usePrice(collection);\n\n return (\n <Link\n href={href}\n className={cn(\n COIN_GRID,\n \"border-b border-border/40 px-2 py-2.5 text-sm transition-colors\",\n \"hover:bg-muted/40 active:bg-muted/60\"\n )}\n >\n <span className=\"flex min-w-0 items-center gap-3\">\n <CoinAvatar collection={collection} />\n <span className=\"min-w-0\">\n <span className=\"block truncate font-semibold leading-tight\">{collection.name ?? \"Untitled coin\"}</span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n {collection.symbol ?? \"—\"}\n {showKind && <span className=\"text-muted-foreground/60\"> · {coinKindLabel(coinKind(collection.service))}</span>}\n </span>\n </span>\n </span>\n\n <PriceCell price={price} status={status} isLoading={isLoading} />\n </Link>\n );\n}\n\nexport function CoinRowSkeleton() {\n return (\n <div className={cn(COIN_GRID, \"border-b border-border/40 px-2 py-2.5\")}>\n <span className=\"flex items-center gap-3\">\n <span className=\"h-9 w-9 shrink-0 animate-pulse rounded-full bg-muted-foreground/20\" />\n <span className=\"space-y-1.5\">\n <span className=\"block h-4 w-28 animate-pulse rounded bg-muted-foreground/20\" />\n <span className=\"block h-3 w-14 animate-pulse rounded bg-muted-foreground/10\" />\n </span>\n </span>\n <span className=\"ml-auto block h-4 w-16 animate-pulse rounded bg-muted-foreground/20\" />\n </div>\n );\n}\n"],"mappings":"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","names":["Image","Link"]}
|
|
@@ -8,7 +8,7 @@ type UseCoinPrice = (collection: CoinCollectionLike) => {
|
|
|
8
8
|
status?: CoinMarketStatus;
|
|
9
9
|
isLoading: boolean;
|
|
10
10
|
};
|
|
11
|
-
declare const COIN_GRID = "grid grid-cols-[minmax(0,1fr)
|
|
11
|
+
declare const COIN_GRID = "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:gap-4";
|
|
12
12
|
interface CoinRowProps {
|
|
13
13
|
collection: CoinCollectionLike;
|
|
14
14
|
usePrice: UseCoinPrice;
|
|
@@ -8,7 +8,7 @@ type UseCoinPrice = (collection: CoinCollectionLike) => {
|
|
|
8
8
|
status?: CoinMarketStatus;
|
|
9
9
|
isLoading: boolean;
|
|
10
10
|
};
|
|
11
|
-
declare const COIN_GRID = "grid grid-cols-[minmax(0,1fr)
|
|
11
|
+
declare const COIN_GRID = "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:gap-4";
|
|
12
12
|
interface CoinRowProps {
|
|
13
13
|
collection: CoinCollectionLike;
|
|
14
14
|
usePrice: UseCoinPrice;
|
|
@@ -4,15 +4,14 @@ import Link from "./link.js";
|
|
|
4
4
|
import Image from "./image.js";
|
|
5
5
|
import { cn } from "../utils/cn.js";
|
|
6
6
|
import { ipfsToHttp } from "../utils/ipfs.js";
|
|
7
|
-
import {
|
|
7
|
+
import { DualPrice } from "./dual-price.js";
|
|
8
8
|
import {
|
|
9
9
|
coinKind,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
coinKindLabel,
|
|
11
|
+
coinAccentToken
|
|
12
12
|
} from "../data/coins.js";
|
|
13
13
|
import { formatUsdPrice } from "../utils/format.js";
|
|
14
|
-
const COIN_GRID = "grid grid-cols-[minmax(0,1fr)
|
|
15
|
-
const kindLabel = (kind) => kind === "creator" ? "Creator Coin" : "Memecoin";
|
|
14
|
+
const COIN_GRID = "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:gap-4";
|
|
16
15
|
function CoinAvatar({ collection, size = 36 }) {
|
|
17
16
|
const logoUri = collection.profile?.image ?? collection.image;
|
|
18
17
|
const logo = logoUri ? ipfsToHttp(logoUri) : null;
|
|
@@ -40,13 +39,15 @@ function PriceCell({ price, status, isLoading }) {
|
|
|
40
39
|
return status === "pre-launch" ? /* @__PURE__ */ jsx("span", { className: "block text-right text-xs text-muted-foreground", children: "Not trading yet" }) : /* @__PURE__ */ jsx("span", { className: "block text-right text-muted-foreground", children: "\u2014" });
|
|
41
40
|
}
|
|
42
41
|
const usd = price.quoteUsdRate != null ? formatUsdPrice(price.quotePerCoin * price.quoteUsdRate) : null;
|
|
43
|
-
return /* @__PURE__ */
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
return /* @__PURE__ */ jsx("span", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
43
|
+
DualPrice,
|
|
44
|
+
{
|
|
45
|
+
amountFormatted: String(price.quotePerCoin),
|
|
46
|
+
currency: price.quoteSymbol,
|
|
47
|
+
usdValue: usd,
|
|
48
|
+
scale: "card"
|
|
49
|
+
}
|
|
50
|
+
) });
|
|
50
51
|
}
|
|
51
52
|
function CoinRow({ collection, usePrice, href, showKind = false }) {
|
|
52
53
|
const { price, status, isLoading } = usePrice(collection);
|
|
@@ -68,7 +69,7 @@ function CoinRow({ collection, usePrice, href, showKind = false }) {
|
|
|
68
69
|
collection.symbol ?? "\u2014",
|
|
69
70
|
showKind && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground/60", children: [
|
|
70
71
|
" \xB7 ",
|
|
71
|
-
|
|
72
|
+
coinKindLabel(coinKind(collection.service))
|
|
72
73
|
] })
|
|
73
74
|
] })
|
|
74
75
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coin-row.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-row.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { DualPrice } from \"./dual-price.js\";\nimport {\n coinKind, coinKindLabel, coinAccentToken,\n type CoinCollectionLike, type CoinPriceLike,\n} from \"../data/coins.js\";\nimport { formatUsdPrice } from \"../utils/format.js\";\n\nexport type CoinMarketStatus = \"live\" | \"pre-launch\" | \"unavailable\";\n\nexport type UseCoinPrice = (collection: CoinCollectionLike) => {\n price: CoinPriceLike | null;\n status?: CoinMarketStatus;\n isLoading: boolean;\n};\n\nexport const COIN_GRID = \"grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:gap-4\";\n\nexport interface CoinRowProps {\n collection: CoinCollectionLike;\n usePrice: UseCoinPrice;\n href: string;\n showKind?: boolean;\n}\n\nexport function CoinAvatar({ collection, size = 36 }: { collection: CoinCollectionLike; size?: number }) {\n const logoUri = collection.profile?.image ?? collection.image;\n const logo = logoUri ? ipfsToHttp(logoUri) : null;\n const initials = (collection.symbol ?? collection.name ?? \"?\").trim().slice(0, 2).toUpperCase();\n const accent = coinAccentToken(collection.symbol ?? collection.name ?? collection.contractAddress);\n\n return (\n <div\n className={cn(\"relative shrink-0 overflow-hidden rounded-full\", accent)}\n style={{ width: size, height: size }}\n >\n {logo ? (\n <Image src={logo} alt=\"\" fill sizes={`${size}px`} className=\"object-cover\" unoptimized />\n ) : (\n <span\n className=\"flex h-full w-full items-center justify-center font-bold leading-none text-white\"\n style={{ fontSize: size * 0.36 }}\n >\n {initials}\n </span>\n )}\n </div>\n );\n}\n\nfunction PriceCell({ price, status, isLoading }: { price: CoinPriceLike | null; status?: CoinMarketStatus; isLoading: boolean }) {\n if (isLoading) return <span className=\"ml-auto block h-4 w-16 animate-pulse rounded bg-muted-foreground/20\" />;\n if (!price) {\n return status === \"pre-launch\" ? (\n <span className=\"block text-right text-xs text-muted-foreground\">Not trading yet</span>\n ) : (\n <span className=\"block text-right text-muted-foreground\">—</span>\n );\n }\n\n const usd = price.quoteUsdRate != null ? formatUsdPrice(price.quotePerCoin * price.quoteUsdRate) : null;\n\n return (\n <span className=\"flex justify-end\">\n <DualPrice\n amountFormatted={String(price.quotePerCoin)}\n currency={price.quoteSymbol}\n usdValue={usd}\n scale=\"card\"\n />\n </span>\n );\n}\n\nexport function CoinRow({ collection, usePrice, href, showKind = false }: CoinRowProps) {\n const { price, status, isLoading } = usePrice(collection);\n\n return (\n <Link\n href={href}\n className={cn(\n COIN_GRID,\n \"border-b border-border/40 px-2 py-2.5 text-sm transition-colors\",\n \"hover:bg-muted/40 active:bg-muted/60\"\n )}\n >\n <span className=\"flex min-w-0 items-center gap-3\">\n <CoinAvatar collection={collection} />\n <span className=\"min-w-0\">\n <span className=\"block truncate font-semibold leading-tight\">{collection.name ?? \"Untitled coin\"}</span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n {collection.symbol ?? \"—\"}\n {showKind && <span className=\"text-muted-foreground/60\"> · {coinKindLabel(coinKind(collection.service))}</span>}\n </span>\n </span>\n </span>\n\n <PriceCell price={price} status={status} isLoading={isLoading} />\n </Link>\n );\n}\n\nexport function CoinRowSkeleton() {\n return (\n <div className={cn(COIN_GRID, \"border-b border-border/40 px-2 py-2.5\")}>\n <span className=\"flex items-center gap-3\">\n <span className=\"h-9 w-9 shrink-0 animate-pulse rounded-full bg-muted-foreground/20\" />\n <span className=\"space-y-1.5\">\n <span className=\"block h-4 w-28 animate-pulse rounded bg-muted-foreground/20\" />\n <span className=\"block h-3 w-14 animate-pulse rounded bg-muted-foreground/10\" />\n </span>\n </span>\n <span className=\"ml-auto block h-4 w-16 animate-pulse rounded bg-muted-foreground/20\" />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -30,8 +30,7 @@ var import_coin_row = require("./coin-row.js");
|
|
|
30
30
|
var import_coins = require("../data/coins.js");
|
|
31
31
|
const FILTER_TABS = [
|
|
32
32
|
{ label: "All", value: "all" },
|
|
33
|
-
{ label:
|
|
34
|
-
{ label: "Memecoins", value: "memecoin" }
|
|
33
|
+
...import_coins.COIN_KINDS.map((kind) => ({ label: (0, import_coins.coinKindLabel)(kind), value: kind }))
|
|
35
34
|
];
|
|
36
35
|
const SORT_OPTIONS = [
|
|
37
36
|
{ label: "Recently launched", value: "recent" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinRow, CoinRowSkeleton, COIN_GRID, type UseCoinPrice } from \"./coin-row.js\";\nimport { coinKind, type CoinCollectionLike } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | \"creator\" | \"memecoin\";\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n { label: \"Creator Coins\", value: \"creator\" },\n { label: \"Memecoins\", value: \"memecoin\" },\n];\n\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n const showKind = useMemo(() => new Set(items.map((c) => coinKind(c.service))).size > 1, [items]);\n\n return (\n <div className=\"space-y-5\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins & memecoins</h1>\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n </div>\n\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>}\n {sort !== \"recent\" && <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>}\n </div>\n )}\n\n <div>\n <div className={cn(COIN_GRID, \"border-b border-border px-2 pb-2 text-2xs font-medium uppercase tracking-wide text-muted-foreground\")}>\n <span>Token</span>\n <span className=\"text-right\">Price</span>\n </div>\n\n {isLoading && items.length === 0 ? (\n Array.from({ length: 6 }).map((_, i) => <CoinRowSkeleton key={i} />)\n ) : items.length === 0 ? (\n <p className=\"py-16 text-center text-sm text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </p>\n ) : (\n items.map((c) => (\n <CoinRow\n key={`${c.chain}-${c.contractAddress}`}\n collection={c}\n usePrice={usePrice}\n href={coinHref(c)}\n showKind={showKind}\n />\n ))\n )}\n </div>\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"relative z-10 w-full max-w-sm space-y-5 overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinRow, CoinRowSkeleton, COIN_GRID, type UseCoinPrice } from \"./coin-row.js\";\nimport { coinKind, coinKindLabel, COIN_KINDS, type CoinCollectionLike, type CoinKind } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | CoinKind;\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n ...COIN_KINDS.map((kind) => ({ label: coinKindLabel(kind), value: kind as CoinFilter })),\n];\n\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n const showKind = useMemo(() => new Set(items.map((c) => coinKind(c.service))).size > 1, [items]);\n\n return (\n <div className=\"space-y-5\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins & memecoins</h1>\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n </div>\n\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>}\n {sort !== \"recent\" && <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>}\n </div>\n )}\n\n <div>\n <div className={cn(COIN_GRID, \"border-b border-border px-2 pb-2 text-2xs font-medium uppercase tracking-wide text-muted-foreground\")}>\n <span>Token</span>\n <span className=\"text-right\">Price</span>\n </div>\n\n {isLoading && items.length === 0 ? (\n Array.from({ length: 6 }).map((_, i) => <CoinRowSkeleton key={i} />)\n ) : items.length === 0 ? (\n <p className=\"py-16 text-center text-sm text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </p>\n ) : (\n items.map((c) => (\n <CoinRow\n key={`${c.chain}-${c.contractAddress}`}\n collection={c}\n usePrice={usePrice}\n href={coinHref(c)}\n showKind={showKind}\n />\n ))\n )}\n </div>\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"relative z-10 w-full max-w-sm space-y-5 overflow-hidden rounded-[calc(var(--radius)*1.25)] bg-card p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { UseCoinPrice } from './coin-row.cjs';
|
|
3
|
-
import { CoinCollectionLike } from '../data/coins.cjs';
|
|
3
|
+
import { CoinKind, CoinCollectionLike } from '../data/coins.cjs';
|
|
4
4
|
import '@medialane/sdk';
|
|
5
5
|
|
|
6
|
-
type CoinFilter = "all" |
|
|
6
|
+
type CoinFilter = "all" | CoinKind;
|
|
7
7
|
type CoinSort = "recent" | "name";
|
|
8
8
|
type UseCoins = (opts: {
|
|
9
9
|
filter: CoinFilter;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { UseCoinPrice } from './coin-row.js';
|
|
3
|
-
import { CoinCollectionLike } from '../data/coins.js';
|
|
3
|
+
import { CoinKind, CoinCollectionLike } from '../data/coins.js';
|
|
4
4
|
import '@medialane/sdk';
|
|
5
5
|
|
|
6
|
-
type CoinFilter = "all" |
|
|
6
|
+
type CoinFilter = "all" | CoinKind;
|
|
7
7
|
type CoinSort = "recent" | "name";
|
|
8
8
|
type UseCoins = (opts: {
|
|
9
9
|
filter: CoinFilter;
|
|
@@ -4,11 +4,10 @@ import { useState, useMemo, useEffect } from "react";
|
|
|
4
4
|
import { Coins, Search, SlidersHorizontal, X } from "lucide-react";
|
|
5
5
|
import { cn } from "../utils/cn.js";
|
|
6
6
|
import { CoinRow, CoinRowSkeleton, COIN_GRID } from "./coin-row.js";
|
|
7
|
-
import { coinKind } from "../data/coins.js";
|
|
7
|
+
import { coinKind, coinKindLabel, COIN_KINDS } from "../data/coins.js";
|
|
8
8
|
const FILTER_TABS = [
|
|
9
9
|
{ label: "All", value: "all" },
|
|
10
|
-
{ label:
|
|
11
|
-
{ label: "Memecoins", value: "memecoin" }
|
|
10
|
+
...COIN_KINDS.map((kind) => ({ label: coinKindLabel(kind), value: kind }))
|
|
12
11
|
];
|
|
13
12
|
const SORT_OPTIONS = [
|
|
14
13
|
{ label: "Recently launched", value: "recent" },
|