@medialane/ui 0.133.0 → 0.134.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/components/badge.d.cts +1 -1
  2. package/dist/components/badge.d.ts +1 -1
  3. package/dist/components/button.d.cts +2 -2
  4. package/dist/components/button.d.ts +2 -2
  5. package/dist/components/coin-row.cjs +129 -0
  6. package/dist/components/coin-row.cjs.map +1 -0
  7. package/dist/components/coin-row.d.cts +22 -0
  8. package/dist/components/coin-row.d.ts +22 -0
  9. package/dist/components/coin-row.js +97 -0
  10. package/dist/components/coin-row.js.map +1 -0
  11. package/dist/components/coins-explorer.cjs +47 -42
  12. package/dist/components/coins-explorer.cjs.map +1 -1
  13. package/dist/components/coins-explorer.d.cts +1 -1
  14. package/dist/components/coins-explorer.d.ts +1 -1
  15. package/dist/components/coins-explorer.js +48 -43
  16. package/dist/components/coins-explorer.js.map +1 -1
  17. package/dist/data/coins.cjs +32 -8
  18. package/dist/data/coins.cjs.map +1 -1
  19. package/dist/data/coins.d.cts +4 -1
  20. package/dist/data/coins.d.ts +4 -1
  21. package/dist/data/coins.js +28 -7
  22. package/dist/data/coins.js.map +1 -1
  23. package/dist/index.cjs +18 -6
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +3 -3
  26. package/dist/index.d.ts +3 -3
  27. package/dist/index.js +14 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/utils/format.cjs +20 -0
  30. package/dist/utils/format.cjs.map +1 -1
  31. package/dist/utils/format.d.cts +3 -1
  32. package/dist/utils/format.d.ts +3 -1
  33. package/dist/utils/format.js +18 -0
  34. package/dist/utils/format.js.map +1 -1
  35. package/package.json +1 -1
  36. package/dist/components/coin-card.cjs +0 -141
  37. package/dist/components/coin-card.cjs.map +0 -1
  38. package/dist/components/coin-card.d.cts +0 -17
  39. package/dist/components/coin-card.d.ts +0 -17
  40. package/dist/components/coin-card.js +0 -108
  41. package/dist/components/coin-card.js.map +0 -1
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState, useMemo, useEffect } from "react";
4
- import { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from "lucide-react";
4
+ import { Coins, Search, SlidersHorizontal, X } from "lucide-react";
5
5
  import { cn } from "../utils/cn.js";
6
- import { CoinCard, CoinRow, CoinCardSkeleton } from "./coin-card.js";
6
+ import { CoinRow, CoinRowSkeleton, COIN_GRID } from "./coin-row.js";
7
+ import { coinKind } from "../data/coins.js";
7
8
  const FILTER_TABS = [
8
9
  { label: "All", value: "all" },
9
10
  { label: "Creator Coins", value: "creator" },
@@ -18,7 +19,6 @@ const sortLabel = (v) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? "";
18
19
  function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
19
20
  const [filter, setFilter] = useState("all");
20
21
  const [sort, setSort] = useState("recent");
21
- const [view, setView] = useState("grid");
22
22
  const [query, setQuery] = useState("");
23
23
  const [filtersOpen, setFiltersOpen] = useState(false);
24
24
  const filterCount = (filter !== "all" ? 1 : 0) + (sort !== "recent" ? 1 : 0);
@@ -36,7 +36,8 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
36
36
  (c) => (c.name ?? "").toLowerCase().includes(q) || (c.symbol ?? "").toLowerCase().includes(q)
37
37
  );
38
38
  }, [collections, query]);
39
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
39
+ const showKind = useMemo(() => new Set(items.map((c) => coinKind(c.service))).size > 1, [items]);
40
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
40
41
  heading && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
41
42
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-primary", children: [
42
43
  /* @__PURE__ */ jsx(Coins, { className: "h-5 w-5" }),
@@ -44,49 +45,53 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
44
45
  ] }),
45
46
  /* @__PURE__ */ jsx("h1", { className: "text-3xl", children: "Creator coins & memecoins" })
46
47
  ] }),
47
- /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-b border-border/60 pb-3", children: [
48
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
49
- /* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
50
- /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
51
- /* @__PURE__ */ jsx(
52
- "input",
53
- {
54
- value: query,
55
- onChange: (e) => setQuery(e.target.value),
56
- placeholder: "Search coins by name or symbol\u2026",
57
- className: "w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50"
58
- }
59
- )
60
- ] }),
61
- /* @__PURE__ */ jsxs(
62
- "button",
48
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
49
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
50
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
51
+ /* @__PURE__ */ jsx(
52
+ "input",
63
53
  {
64
- onClick: () => setFiltersOpen(true),
65
- 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",
66
- children: [
67
- /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
68
- "Filters",
69
- filterCount > 0 && /* @__PURE__ */ jsx("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", children: filterCount })
70
- ]
54
+ value: query,
55
+ onChange: (e) => setQuery(e.target.value),
56
+ placeholder: "Search coins by name or symbol\u2026",
57
+ className: "w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50"
71
58
  }
72
- ),
73
- /* @__PURE__ */ jsx("div", { className: "inline-flex shrink-0 rounded-lg border border-border p-0.5", children: [{ v: "grid", Icon: LayoutGrid }, { v: "table", Icon: List }].map(({ v, Icon }) => /* @__PURE__ */ jsx(
74
- "button",
75
- {
76
- onClick: () => setView(v),
77
- "aria-label": v === "grid" ? "Grid view" : "Table view",
78
- className: cn("rounded-md p-1.5 transition-colors", view === v ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground"),
79
- children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" })
80
- },
81
- v
82
- )) })
59
+ )
83
60
  ] }),
84
- filterCount > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
85
- filter !== "all" && /* @__PURE__ */ jsx(Chip, { onClear: () => setFilter("all"), children: filterLabel(filter) }),
86
- sort !== "recent" && /* @__PURE__ */ jsx(Chip, { onClear: () => setSort("recent"), children: sortLabel(sort) })
87
- ] })
61
+ /* @__PURE__ */ jsxs(
62
+ "button",
63
+ {
64
+ onClick: () => setFiltersOpen(true),
65
+ 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",
66
+ children: [
67
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
68
+ "Filters",
69
+ filterCount > 0 && /* @__PURE__ */ jsx("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", children: filterCount })
70
+ ]
71
+ }
72
+ )
73
+ ] }),
74
+ filterCount > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
75
+ filter !== "all" && /* @__PURE__ */ jsx(Chip, { onClear: () => setFilter("all"), children: filterLabel(filter) }),
76
+ sort !== "recent" && /* @__PURE__ */ jsx(Chip, { onClear: () => setSort("recent"), children: sortLabel(sort) })
77
+ ] }),
78
+ /* @__PURE__ */ jsxs("div", { children: [
79
+ /* @__PURE__ */ jsxs("div", { className: cn(COIN_GRID, "border-b border-border px-2 pb-2 text-2xs font-medium uppercase tracking-wide text-muted-foreground"), children: [
80
+ /* @__PURE__ */ jsx("span", { children: "Token" }),
81
+ /* @__PURE__ */ jsx("span", { className: "text-right", children: "Price" }),
82
+ /* @__PURE__ */ jsx("span", { className: "hidden text-right sm:block", children: "FDV" })
83
+ ] }),
84
+ isLoading && items.length === 0 ? Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(CoinRowSkeleton, {}, i)) : items.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-16 text-center text-sm text-muted-foreground", children: query.trim() ? `No coins match "${query.trim()}".` : "No coins yet." }) : items.map((c) => /* @__PURE__ */ jsx(
85
+ CoinRow,
86
+ {
87
+ collection: c,
88
+ usePrice,
89
+ href: coinHref(c),
90
+ showKind
91
+ },
92
+ `${c.chain}-${c.contractAddress}`
93
+ ))
88
94
  ] }),
89
- isLoading && items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4", children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(CoinCardSkeleton, {}, i)) }) : items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-border/60 py-16 text-center text-muted-foreground", children: query.trim() ? `No coins match "${query.trim()}".` : "No coins yet." }) : view === "table" ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: items.map((c) => /* @__PURE__ */ jsx(CoinRow, { collection: c, usePrice, href: coinHref(c) }, `${c.chain}-${c.contractAddress}`)) }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4", children: items.map((c) => /* @__PURE__ */ jsx(CoinCard, { collection: c, usePrice, href: coinHref(c) }, `${c.chain}-${c.contractAddress}`)) }),
90
95
  filtersOpen && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", role: "dialog", "aria-modal": "true", "aria-label": "Filters", children: [
91
96
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-background/70 backdrop-blur-sm", onClick: () => setFiltersOpen(false) }),
92
97
  /* @__PURE__ */ jsxs("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", children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinCard, CoinRow, CoinCardSkeleton, type UseCoinPrice } from \"./coin-card.js\";\nimport 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 [view, setView] = useState<\"grid\" | \"table\">(\"grid\");\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 return (\n <div className=\"space-y-6\">\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 &amp; memecoins</h1>\n </div>\n )}\n\n <div className=\"space-y-3 border-b border-border/60 pb-3\">\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 className=\"inline-flex shrink-0 rounded-lg border border-border p-0.5\">\n {([{ v: \"grid\", Icon: LayoutGrid }, { v: \"table\", Icon: List }] as const).map(({ v, Icon }) => (\n <button\n key={v}\n onClick={() => setView(v)}\n aria-label={v === \"grid\" ? \"Grid view\" : \"Table view\"}\n className={cn(\"rounded-md p-1.5 transition-colors\", view === v ? \"bg-primary/10 text-primary\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon className=\"h-4 w-4\" />\n </button>\n ))}\n </div>\n </div>\n\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && (\n <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>\n )}\n {sort !== \"recent\" && (\n <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>\n )}\n </div>\n )}\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 8 }).map((_, i) => <CoinCardSkeleton key={i} />)}\n </div>\n ) : items.length === 0 ? (\n <div className=\"rounded-xl border border-border/60 py-16 text-center text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </div>\n ) : view === \"table\" ? (\n <div className=\"space-y-2\">\n {items.map((c) => <CoinRow key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {items.map((c) => <CoinCard key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n )}\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, 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 &amp; 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 <span className=\"hidden text-right sm:block\">FDV</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":[]}
@@ -18,31 +18,55 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var coins_exports = {};
20
20
  __export(coins_exports, {
21
+ coinAccentHue: () => coinAccentHue,
21
22
  coinKind: () => coinKind,
23
+ fdvUsd: () => fdvUsd,
22
24
  formatCoinPrice: () => formatCoinPrice,
23
- formatFdv: () => formatFdv
25
+ formatFdv: () => formatFdv,
26
+ formatFdvUsd: () => formatFdvUsd
24
27
  });
25
28
  module.exports = __toCommonJS(coins_exports);
29
+ var import_format = require("../utils/format.js");
26
30
  function coinKind(service) {
27
31
  return service === "external-erc20" ? "memecoin" : "creator";
28
32
  }
29
33
  function formatCoinPrice(n) {
30
- if (n === 0) return "0";
31
- if (n < 1e-6) return n.toExponential(2);
32
- if (n < 1) return n.toPrecision(3);
33
- return n.toLocaleString(void 0, { maximumFractionDigits: 4 });
34
+ return (0, import_format.formatSmallDecimal)(n);
35
+ }
36
+ const ACCENT_HUES = [220, 258, 341, 23, 325];
37
+ function coinAccentHue(seed) {
38
+ const s = (seed ?? "?").trim().toUpperCase();
39
+ let h = 0;
40
+ for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) >>> 0;
41
+ return ACCENT_HUES[h % ACCENT_HUES.length];
42
+ }
43
+ function abbreviate(n) {
44
+ if (n >= 1e9) return `${(n / 1e9).toLocaleString(void 0, { maximumFractionDigits: 1 })}B`;
45
+ if (n >= 1e6) return `${(n / 1e6).toLocaleString(void 0, { maximumFractionDigits: 1 })}M`;
46
+ if (n >= 1e3) return `${(n / 1e3).toLocaleString(void 0, { maximumFractionDigits: 1 })}K`;
47
+ return n.toLocaleString(void 0, { maximumFractionDigits: 2 });
34
48
  }
35
49
  function formatFdv(quotePerCoin, totalSupply, quoteSymbol) {
36
50
  if (quotePerCoin == null || !totalSupply) return null;
37
- const fdv = quotePerCoin * totalSupply;
38
51
  const sym = quoteSymbol ?? "";
39
- const abbr = fdv >= 1e9 ? `${(fdv / 1e9).toLocaleString(void 0, { maximumFractionDigits: 1 })}B` : fdv >= 1e6 ? `${(fdv / 1e6).toLocaleString(void 0, { maximumFractionDigits: 1 })}M` : fdv >= 1e3 ? `${(fdv / 1e3).toLocaleString(void 0, { maximumFractionDigits: 1 })}K` : fdv.toLocaleString(void 0, { maximumFractionDigits: 2 });
52
+ const abbr = abbreviate(quotePerCoin * totalSupply);
40
53
  return sym ? `${abbr} ${sym}` : abbr;
41
54
  }
55
+ function fdvUsd(price, totalSupply) {
56
+ if (!price || !totalSupply || price.quoteUsdRate == null) return null;
57
+ return price.quotePerCoin * totalSupply * price.quoteUsdRate;
58
+ }
59
+ function formatFdvUsd(price, totalSupply) {
60
+ const v = fdvUsd(price, totalSupply);
61
+ return v == null ? null : `$${abbreviate(v)}`;
62
+ }
42
63
  // Annotate the CommonJS export names for ESM import in node:
43
64
  0 && (module.exports = {
65
+ coinAccentHue,
44
66
  coinKind,
67
+ fdvUsd,
45
68
  formatCoinPrice,
46
- formatFdv
69
+ formatFdv,
70
+ formatFdvUsd
47
71
  });
48
72
  //# sourceMappingURL=coins.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data/coins.ts"],"sourcesContent":["\n\nexport type CoinKind = \"creator\" | \"memecoin\";\n\nexport interface CoinCollectionLike {\n contractAddress: string;\n chain?: string | null;\n name?: string | null;\n symbol?: string | null;\n image?: string | null;\n service?: string | null;\n claimedBy?: string | null;\n holderCount?: number | null;\n totalSupply?: number | null;\n profile?: { image?: string | null } | null;\n}\n\nexport interface CoinPriceLike {\n quotePerCoin: number;\n quoteSymbol: string | null;\n /** USD value of one unit of quoteSymbol, when known — lets price\n * displays show a fiat-equivalent alongside the on-chain quote. */\n quoteUsdRate?: number | null;\n}\n\nexport function coinKind(service: string | null | undefined): CoinKind {\n return service === \"external-erc20\" ? \"memecoin\" : \"creator\";\n}\n\nexport function formatCoinPrice(n: number): string {\n if (n === 0) return \"0\";\n if (n < 0.000001) return n.toExponential(2);\n if (n < 1) return n.toPrecision(3);\n return n.toLocaleString(undefined, { maximumFractionDigits: 4 });\n}\n\nexport function formatFdv(\n quotePerCoin: number | null | undefined,\n totalSupply: number | null | undefined,\n quoteSymbol: string | null | undefined\n): string | null {\n if (quotePerCoin == null || !totalSupply) return null;\n const fdv = quotePerCoin * totalSupply;\n const sym = quoteSymbol ?? \"\";\n const abbr =\n fdv >= 1_000_000_000 ? `${(fdv / 1_000_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}B` :\n fdv >= 1_000_000 ? `${(fdv / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}M` :\n fdv >= 1_000 ? `${(fdv / 1_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}K` :\n fdv.toLocaleString(undefined, { maximumFractionDigits: 2 });\n return sym ? `${abbr} ${sym}` : abbr;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBO,SAAS,SAAS,SAA8C;AACrE,SAAO,YAAY,mBAAmB,aAAa;AACrD;AAEO,SAAS,gBAAgB,GAAmB;AACjD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,IAAI,KAAU,QAAO,EAAE,cAAc,CAAC;AAC1C,MAAI,IAAI,EAAG,QAAO,EAAE,YAAY,CAAC;AACjC,SAAO,EAAE,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC;AACjE;AAEO,SAAS,UACd,cACA,aACA,aACe;AACf,MAAI,gBAAgB,QAAQ,CAAC,YAAa,QAAO;AACjD,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,eAAe;AAC3B,QAAM,OACJ,OAAO,MAAgB,IAAI,MAAM,KAAe,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC,MACvG,OAAO,MAAgB,IAAI,MAAM,KAAW,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC,MACnG,OAAO,MAAgB,IAAI,MAAM,KAAO,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC,MACxE,IAAI,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC;AACnF,SAAO,MAAM,GAAG,IAAI,IAAI,GAAG,KAAK;AAClC;","names":[]}
1
+ {"version":3,"sources":["../../src/data/coins.ts"],"sourcesContent":["\n\nimport { formatSmallDecimal } from \"../utils/format.js\";\n\nexport type CoinKind = \"creator\" | \"memecoin\";\n\nexport interface CoinCollectionLike {\n contractAddress: string;\n chain?: string | null;\n name?: string | null;\n symbol?: string | null;\n image?: string | null;\n service?: string | null;\n claimedBy?: string | null;\n holderCount?: number | null;\n totalSupply?: number | null;\n profile?: { image?: string | null } | null;\n}\n\nexport interface CoinPriceLike {\n quotePerCoin: number;\n quoteSymbol: string | null;\n /** USD value of one unit of quoteSymbol, when known — lets price\n * displays show a fiat-equivalent alongside the on-chain quote. */\n quoteUsdRate?: number | null;\n}\n\nexport function coinKind(service: string | null | undefined): CoinKind {\n return service === \"external-erc20\" ? \"memecoin\" : \"creator\";\n}\n\nexport function formatCoinPrice(n: number): string {\n return formatSmallDecimal(n);\n}\n\nconst ACCENT_HUES = [220, 258, 341, 23, 325];\n\nexport function coinAccentHue(seed: string | null | undefined): number {\n const s = (seed ?? \"?\").trim().toUpperCase();\n let h = 0;\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;\n return ACCENT_HUES[h % ACCENT_HUES.length];\n}\n\nfunction abbreviate(n: number): string {\n if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}B`;\n if (n >= 1_000_000) return `${(n / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}M`;\n if (n >= 1_000) return `${(n / 1_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}K`;\n return n.toLocaleString(undefined, { maximumFractionDigits: 2 });\n}\n\nexport function formatFdv(\n quotePerCoin: number | null | undefined,\n totalSupply: number | null | undefined,\n quoteSymbol: string | null | undefined\n): string | null {\n if (quotePerCoin == null || !totalSupply) return null;\n const sym = quoteSymbol ?? \"\";\n const abbr = abbreviate(quotePerCoin * totalSupply);\n return sym ? `${abbr} ${sym}` : abbr;\n}\n\nexport function fdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): number | null {\n if (!price || !totalSupply || price.quoteUsdRate == null) return null;\n return price.quotePerCoin * totalSupply * price.quoteUsdRate;\n}\n\nexport function formatFdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): string | null {\n const v = fdvUsd(price, totalSupply);\n return v == null ? null : `$${abbreviate(v)}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,oBAAmC;AAyB5B,SAAS,SAAS,SAA8C;AACrE,SAAO,YAAY,mBAAmB,aAAa;AACrD;AAEO,SAAS,gBAAgB,GAAmB;AACjD,aAAO,kCAAmB,CAAC;AAC7B;AAEA,MAAM,cAAc,CAAC,KAAK,KAAK,KAAK,IAAI,GAAG;AAEpC,SAAS,cAAc,MAAyC;AACrE,QAAM,KAAK,QAAQ,KAAK,KAAK,EAAE,YAAY;AAC3C,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,KAAK,IAAI,KAAK,EAAE,WAAW,CAAC,MAAO;AACtE,SAAO,YAAY,IAAI,YAAY,MAAM;AAC3C;AAEA,SAAS,WAAW,GAAmB;AACrC,MAAI,KAAK,IAAe,QAAO,IAAI,IAAI,KAAe,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAC7G,MAAI,KAAK,IAAW,QAAO,IAAI,IAAI,KAAW,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC;AACrG,MAAI,KAAK,IAAO,QAAO,IAAI,IAAI,KAAO,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAC7F,SAAO,EAAE,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC;AACjE;AAEO,SAAS,UACd,cACA,aACA,aACe;AACf,MAAI,gBAAgB,QAAQ,CAAC,YAAa,QAAO;AACjD,QAAM,MAAM,eAAe;AAC3B,QAAM,OAAO,WAAW,eAAe,WAAW;AAClD,SAAO,MAAM,GAAG,IAAI,IAAI,GAAG,KAAK;AAClC;AAEO,SAAS,OAAO,OAA6B,aAAuD;AACzG,MAAI,CAAC,SAAS,CAAC,eAAe,MAAM,gBAAgB,KAAM,QAAO;AACjE,SAAO,MAAM,eAAe,cAAc,MAAM;AAClD;AAEO,SAAS,aAAa,OAA6B,aAAuD;AAC/G,QAAM,IAAI,OAAO,OAAO,WAAW;AACnC,SAAO,KAAK,OAAO,OAAO,IAAI,WAAW,CAAC,CAAC;AAC7C;","names":[]}
@@ -22,6 +22,9 @@ interface CoinPriceLike {
22
22
  }
23
23
  declare function coinKind(service: string | null | undefined): CoinKind;
24
24
  declare function formatCoinPrice(n: number): string;
25
+ declare function coinAccentHue(seed: string | null | undefined): number;
25
26
  declare function formatFdv(quotePerCoin: number | null | undefined, totalSupply: number | null | undefined, quoteSymbol: string | null | undefined): string | null;
27
+ declare function fdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): number | null;
28
+ declare function formatFdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): string | null;
26
29
 
27
- export { type CoinCollectionLike, type CoinKind, type CoinPriceLike, coinKind, formatCoinPrice, formatFdv };
30
+ export { type CoinCollectionLike, type CoinKind, type CoinPriceLike, coinAccentHue, coinKind, fdvUsd, formatCoinPrice, formatFdv, formatFdvUsd };
@@ -22,6 +22,9 @@ interface CoinPriceLike {
22
22
  }
23
23
  declare function coinKind(service: string | null | undefined): CoinKind;
24
24
  declare function formatCoinPrice(n: number): string;
25
+ declare function coinAccentHue(seed: string | null | undefined): number;
25
26
  declare function formatFdv(quotePerCoin: number | null | undefined, totalSupply: number | null | undefined, quoteSymbol: string | null | undefined): string | null;
27
+ declare function fdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): number | null;
28
+ declare function formatFdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): string | null;
26
29
 
27
- export { type CoinCollectionLike, type CoinKind, type CoinPriceLike, coinKind, formatCoinPrice, formatFdv };
30
+ export { type CoinCollectionLike, type CoinKind, type CoinPriceLike, coinAccentHue, coinKind, fdvUsd, formatCoinPrice, formatFdv, formatFdvUsd };
@@ -1,22 +1,43 @@
1
+ import { formatSmallDecimal } from "../utils/format.js";
1
2
  function coinKind(service) {
2
3
  return service === "external-erc20" ? "memecoin" : "creator";
3
4
  }
4
5
  function formatCoinPrice(n) {
5
- if (n === 0) return "0";
6
- if (n < 1e-6) return n.toExponential(2);
7
- if (n < 1) return n.toPrecision(3);
8
- return n.toLocaleString(void 0, { maximumFractionDigits: 4 });
6
+ return formatSmallDecimal(n);
7
+ }
8
+ const ACCENT_HUES = [220, 258, 341, 23, 325];
9
+ function coinAccentHue(seed) {
10
+ const s = (seed ?? "?").trim().toUpperCase();
11
+ let h = 0;
12
+ for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) >>> 0;
13
+ return ACCENT_HUES[h % ACCENT_HUES.length];
14
+ }
15
+ function abbreviate(n) {
16
+ if (n >= 1e9) return `${(n / 1e9).toLocaleString(void 0, { maximumFractionDigits: 1 })}B`;
17
+ if (n >= 1e6) return `${(n / 1e6).toLocaleString(void 0, { maximumFractionDigits: 1 })}M`;
18
+ if (n >= 1e3) return `${(n / 1e3).toLocaleString(void 0, { maximumFractionDigits: 1 })}K`;
19
+ return n.toLocaleString(void 0, { maximumFractionDigits: 2 });
9
20
  }
10
21
  function formatFdv(quotePerCoin, totalSupply, quoteSymbol) {
11
22
  if (quotePerCoin == null || !totalSupply) return null;
12
- const fdv = quotePerCoin * totalSupply;
13
23
  const sym = quoteSymbol ?? "";
14
- const abbr = fdv >= 1e9 ? `${(fdv / 1e9).toLocaleString(void 0, { maximumFractionDigits: 1 })}B` : fdv >= 1e6 ? `${(fdv / 1e6).toLocaleString(void 0, { maximumFractionDigits: 1 })}M` : fdv >= 1e3 ? `${(fdv / 1e3).toLocaleString(void 0, { maximumFractionDigits: 1 })}K` : fdv.toLocaleString(void 0, { maximumFractionDigits: 2 });
24
+ const abbr = abbreviate(quotePerCoin * totalSupply);
15
25
  return sym ? `${abbr} ${sym}` : abbr;
16
26
  }
27
+ function fdvUsd(price, totalSupply) {
28
+ if (!price || !totalSupply || price.quoteUsdRate == null) return null;
29
+ return price.quotePerCoin * totalSupply * price.quoteUsdRate;
30
+ }
31
+ function formatFdvUsd(price, totalSupply) {
32
+ const v = fdvUsd(price, totalSupply);
33
+ return v == null ? null : `$${abbreviate(v)}`;
34
+ }
17
35
  export {
36
+ coinAccentHue,
18
37
  coinKind,
38
+ fdvUsd,
19
39
  formatCoinPrice,
20
- formatFdv
40
+ formatFdv,
41
+ formatFdvUsd
21
42
  };
22
43
  //# sourceMappingURL=coins.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data/coins.ts"],"sourcesContent":["\n\nexport type CoinKind = \"creator\" | \"memecoin\";\n\nexport interface CoinCollectionLike {\n contractAddress: string;\n chain?: string | null;\n name?: string | null;\n symbol?: string | null;\n image?: string | null;\n service?: string | null;\n claimedBy?: string | null;\n holderCount?: number | null;\n totalSupply?: number | null;\n profile?: { image?: string | null } | null;\n}\n\nexport interface CoinPriceLike {\n quotePerCoin: number;\n quoteSymbol: string | null;\n /** USD value of one unit of quoteSymbol, when known — lets price\n * displays show a fiat-equivalent alongside the on-chain quote. */\n quoteUsdRate?: number | null;\n}\n\nexport function coinKind(service: string | null | undefined): CoinKind {\n return service === \"external-erc20\" ? \"memecoin\" : \"creator\";\n}\n\nexport function formatCoinPrice(n: number): string {\n if (n === 0) return \"0\";\n if (n < 0.000001) return n.toExponential(2);\n if (n < 1) return n.toPrecision(3);\n return n.toLocaleString(undefined, { maximumFractionDigits: 4 });\n}\n\nexport function formatFdv(\n quotePerCoin: number | null | undefined,\n totalSupply: number | null | undefined,\n quoteSymbol: string | null | undefined\n): string | null {\n if (quotePerCoin == null || !totalSupply) return null;\n const fdv = quotePerCoin * totalSupply;\n const sym = quoteSymbol ?? \"\";\n const abbr =\n fdv >= 1_000_000_000 ? `${(fdv / 1_000_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}B` :\n fdv >= 1_000_000 ? `${(fdv / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}M` :\n fdv >= 1_000 ? `${(fdv / 1_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}K` :\n fdv.toLocaleString(undefined, { maximumFractionDigits: 2 });\n return sym ? `${abbr} ${sym}` : abbr;\n}\n"],"mappings":"AAyBO,SAAS,SAAS,SAA8C;AACrE,SAAO,YAAY,mBAAmB,aAAa;AACrD;AAEO,SAAS,gBAAgB,GAAmB;AACjD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,IAAI,KAAU,QAAO,EAAE,cAAc,CAAC;AAC1C,MAAI,IAAI,EAAG,QAAO,EAAE,YAAY,CAAC;AACjC,SAAO,EAAE,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC;AACjE;AAEO,SAAS,UACd,cACA,aACA,aACe;AACf,MAAI,gBAAgB,QAAQ,CAAC,YAAa,QAAO;AACjD,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,eAAe;AAC3B,QAAM,OACJ,OAAO,MAAgB,IAAI,MAAM,KAAe,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC,MACvG,OAAO,MAAgB,IAAI,MAAM,KAAW,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC,MACnG,OAAO,MAAgB,IAAI,MAAM,KAAO,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC,MACxE,IAAI,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC;AACnF,SAAO,MAAM,GAAG,IAAI,IAAI,GAAG,KAAK;AAClC;","names":[]}
1
+ {"version":3,"sources":["../../src/data/coins.ts"],"sourcesContent":["\n\nimport { formatSmallDecimal } from \"../utils/format.js\";\n\nexport type CoinKind = \"creator\" | \"memecoin\";\n\nexport interface CoinCollectionLike {\n contractAddress: string;\n chain?: string | null;\n name?: string | null;\n symbol?: string | null;\n image?: string | null;\n service?: string | null;\n claimedBy?: string | null;\n holderCount?: number | null;\n totalSupply?: number | null;\n profile?: { image?: string | null } | null;\n}\n\nexport interface CoinPriceLike {\n quotePerCoin: number;\n quoteSymbol: string | null;\n /** USD value of one unit of quoteSymbol, when known — lets price\n * displays show a fiat-equivalent alongside the on-chain quote. */\n quoteUsdRate?: number | null;\n}\n\nexport function coinKind(service: string | null | undefined): CoinKind {\n return service === \"external-erc20\" ? \"memecoin\" : \"creator\";\n}\n\nexport function formatCoinPrice(n: number): string {\n return formatSmallDecimal(n);\n}\n\nconst ACCENT_HUES = [220, 258, 341, 23, 325];\n\nexport function coinAccentHue(seed: string | null | undefined): number {\n const s = (seed ?? \"?\").trim().toUpperCase();\n let h = 0;\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;\n return ACCENT_HUES[h % ACCENT_HUES.length];\n}\n\nfunction abbreviate(n: number): string {\n if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}B`;\n if (n >= 1_000_000) return `${(n / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}M`;\n if (n >= 1_000) return `${(n / 1_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}K`;\n return n.toLocaleString(undefined, { maximumFractionDigits: 2 });\n}\n\nexport function formatFdv(\n quotePerCoin: number | null | undefined,\n totalSupply: number | null | undefined,\n quoteSymbol: string | null | undefined\n): string | null {\n if (quotePerCoin == null || !totalSupply) return null;\n const sym = quoteSymbol ?? \"\";\n const abbr = abbreviate(quotePerCoin * totalSupply);\n return sym ? `${abbr} ${sym}` : abbr;\n}\n\nexport function fdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): number | null {\n if (!price || !totalSupply || price.quoteUsdRate == null) return null;\n return price.quotePerCoin * totalSupply * price.quoteUsdRate;\n}\n\nexport function formatFdvUsd(price: CoinPriceLike | null, totalSupply: number | null | undefined): string | null {\n const v = fdvUsd(price, totalSupply);\n return v == null ? null : `$${abbreviate(v)}`;\n}\n"],"mappings":"AAEA,SAAS,0BAA0B;AAyB5B,SAAS,SAAS,SAA8C;AACrE,SAAO,YAAY,mBAAmB,aAAa;AACrD;AAEO,SAAS,gBAAgB,GAAmB;AACjD,SAAO,mBAAmB,CAAC;AAC7B;AAEA,MAAM,cAAc,CAAC,KAAK,KAAK,KAAK,IAAI,GAAG;AAEpC,SAAS,cAAc,MAAyC;AACrE,QAAM,KAAK,QAAQ,KAAK,KAAK,EAAE,YAAY;AAC3C,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,KAAK,IAAI,KAAK,EAAE,WAAW,CAAC,MAAO;AACtE,SAAO,YAAY,IAAI,YAAY,MAAM;AAC3C;AAEA,SAAS,WAAW,GAAmB;AACrC,MAAI,KAAK,IAAe,QAAO,IAAI,IAAI,KAAe,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAC7G,MAAI,KAAK,IAAW,QAAO,IAAI,IAAI,KAAW,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC;AACrG,MAAI,KAAK,IAAO,QAAO,IAAI,IAAI,KAAO,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAC7F,SAAO,EAAE,eAAe,QAAW,EAAE,uBAAuB,EAAE,CAAC;AACjE;AAEO,SAAS,UACd,cACA,aACA,aACe;AACf,MAAI,gBAAgB,QAAQ,CAAC,YAAa,QAAO;AACjD,QAAM,MAAM,eAAe;AAC3B,QAAM,OAAO,WAAW,eAAe,WAAW;AAClD,SAAO,MAAM,GAAG,IAAI,IAAI,GAAG,KAAK;AAClC;AAEO,SAAS,OAAO,OAA6B,aAAuD;AACzG,MAAI,CAAC,SAAS,CAAC,eAAe,MAAM,gBAAgB,KAAM,QAAO;AACjE,SAAO,MAAM,eAAe,cAAc,MAAM;AAClD;AAEO,SAAS,aAAa,OAA6B,aAAuD;AAC/G,QAAM,IAAI,OAAO,OAAO,WAAW;AACnC,SAAO,KAAK,OAAO,OAAO,IAAI,WAAW,CAAC,CAAC;AAC7C;","names":[]}
package/dist/index.cjs CHANGED
@@ -55,6 +55,7 @@ __export(index_exports, {
55
55
  BadgeShelf: () => import_badge_shelf.BadgeShelf,
56
56
  BadgeUnlockToastContent: () => import_badge_unlock_toast_content.BadgeUnlockToastContent,
57
57
  Button: () => import_button.Button,
58
+ COIN_GRID: () => import_coin_row.COIN_GRID,
58
59
  Card: () => import_card.Card,
59
60
  CardContent: () => import_card.CardContent,
60
61
  CardDescription: () => import_card.CardDescription,
@@ -64,10 +65,10 @@ __export(index_exports, {
64
65
  Checkbox: () => import_checkbox.Checkbox,
65
66
  ClaimRail: () => import_claim_rail.ClaimRail,
66
67
  ClubOwnerActions: () => import_club_owner_actions.ClubOwnerActions,
67
- CoinCard: () => import_coin_card.CoinCard,
68
- CoinCardSkeleton: () => import_coin_card.CoinCardSkeleton,
68
+ CoinAvatar: () => import_coin_row.CoinAvatar,
69
69
  CoinLaunchPreview: () => import_coin_launch_preview.CoinLaunchPreview,
70
- CoinRow: () => import_coin_card.CoinRow,
70
+ CoinRow: () => import_coin_row.CoinRow,
71
+ CoinRowSkeleton: () => import_coin_row.CoinRowSkeleton,
71
72
  CoinsExplorer: () => import_coins_explorer.CoinsExplorer,
72
73
  Collapsible: () => import_collapsible.Collapsible,
73
74
  CollapsibleContent: () => import_collapsible.CollapsibleContent,
@@ -276,18 +277,23 @@ __export(index_exports, {
276
277
  buildEditionStats: () => import_asset_top_sections.buildEditionStats,
277
278
  buttonVariants: () => import_button.buttonVariants,
278
279
  cn: () => import_cn.cn,
280
+ coinAccentHue: () => import_coins.coinAccentHue,
279
281
  coinKind: () => import_coins.coinKind,
280
282
  createRewardToast: () => import_reward_toast.createRewardToast,
281
283
  derivePortfolioCounts: () => import_portfolio_counts.derivePortfolioCounts,
282
284
  dropCreateSchema: () => import_drop_create_schema.dropCreateSchema,
285
+ fdvUsd: () => import_coins.fdvUsd,
283
286
  formatActivity: () => import_format_activity.formatActivity,
284
287
  formatAssetReceivedNotification: () => import_format_activity.formatAssetReceivedNotification,
285
288
  formatCoinPrice: () => import_coins.formatCoinPrice,
286
289
  formatDisplayPrice: () => import_format.formatDisplayPrice,
287
290
  formatFdv: () => import_coins.formatFdv,
291
+ formatFdvUsd: () => import_coins.formatFdvUsd,
288
292
  formatOfferAcceptedNotification: () => import_format_activity.formatOfferAcceptedNotification,
289
293
  formatOrderNotification: () => import_format_activity.formatOrderNotification,
294
+ formatSmallDecimal: () => import_format.formatSmallDecimal,
290
295
  formatUsd: () => import_format.formatUsd,
296
+ formatUsdPrice: () => import_format.formatUsdPrice,
291
297
  getDefaultClaimWindow: () => import_launchpad_defaults.getDefaultClaimWindow,
292
298
  getDefaultDropSchedule: () => import_launchpad_defaults.getDefaultDropSchedule,
293
299
  getFriendlyWalletError: () => import_wallet_error.getFriendlyWalletError,
@@ -389,7 +395,7 @@ var import_asset_picker = require("./components/asset-picker.js");
389
395
  var import_asset_search_picker = require("./components/asset-search-picker.js");
390
396
  var import_license_terms_builder = require("./components/license-terms-builder.js");
391
397
  var import_coins = require("./data/coins.js");
392
- var import_coin_card = require("./components/coin-card.js");
398
+ var import_coin_row = require("./components/coin-row.js");
393
399
  var import_coins_explorer = require("./components/coins-explorer.js");
394
400
  var import_time = require("./utils/time.js");
395
401
  var import_activity = require("./data/activity.js");
@@ -544,6 +550,7 @@ var import_dialog = require("./components/dialog.js");
544
550
  BadgeShelf,
545
551
  BadgeUnlockToastContent,
546
552
  Button,
553
+ COIN_GRID,
547
554
  Card,
548
555
  CardContent,
549
556
  CardDescription,
@@ -553,10 +560,10 @@ var import_dialog = require("./components/dialog.js");
553
560
  Checkbox,
554
561
  ClaimRail,
555
562
  ClubOwnerActions,
556
- CoinCard,
557
- CoinCardSkeleton,
563
+ CoinAvatar,
558
564
  CoinLaunchPreview,
559
565
  CoinRow,
566
+ CoinRowSkeleton,
560
567
  CoinsExplorer,
561
568
  Collapsible,
562
569
  CollapsibleContent,
@@ -765,18 +772,23 @@ var import_dialog = require("./components/dialog.js");
765
772
  buildEditionStats,
766
773
  buttonVariants,
767
774
  cn,
775
+ coinAccentHue,
768
776
  coinKind,
769
777
  createRewardToast,
770
778
  derivePortfolioCounts,
771
779
  dropCreateSchema,
780
+ fdvUsd,
772
781
  formatActivity,
773
782
  formatAssetReceivedNotification,
774
783
  formatCoinPrice,
775
784
  formatDisplayPrice,
776
785
  formatFdv,
786
+ formatFdvUsd,
777
787
  formatOfferAcceptedNotification,
778
788
  formatOrderNotification,
789
+ formatSmallDecimal,
779
790
  formatUsd,
791
+ formatUsdPrice,
780
792
  getDefaultClaimWindow,
781
793
  getDefaultDropSchedule,
782
794
  getFriendlyWalletError,