@medialane/ui 0.137.1 → 0.138.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 (37) hide show
  1. package/dist/components/coin-row.cjs +10 -2
  2. package/dist/components/coin-row.cjs.map +1 -1
  3. package/dist/components/coin-row.js +10 -2
  4. package/dist/components/coin-row.js.map +1 -1
  5. package/dist/components/coins-explorer.cjs +46 -105
  6. package/dist/components/coins-explorer.cjs.map +1 -1
  7. package/dist/components/coins-explorer.d.cts +7 -2
  8. package/dist/components/coins-explorer.d.ts +7 -2
  9. package/dist/components/coins-explorer.js +49 -108
  10. package/dist/components/coins-explorer.js.map +1 -1
  11. package/dist/components/dual-price.cjs +17 -14
  12. package/dist/components/dual-price.cjs.map +1 -1
  13. package/dist/components/dual-price.d.cts +2 -1
  14. package/dist/components/dual-price.d.ts +2 -1
  15. package/dist/components/dual-price.js +17 -14
  16. package/dist/components/dual-price.js.map +1 -1
  17. package/dist/components/stat-tile.cjs +23 -15
  18. package/dist/components/stat-tile.cjs.map +1 -1
  19. package/dist/components/stat-tile.d.cts +6 -2
  20. package/dist/components/stat-tile.d.ts +6 -2
  21. package/dist/components/stat-tile.js +23 -15
  22. package/dist/components/stat-tile.js.map +1 -1
  23. package/dist/data/coin-order.cjs +43 -0
  24. package/dist/data/coin-order.cjs.map +1 -0
  25. package/dist/data/coin-order.d.cts +6 -0
  26. package/dist/data/coin-order.d.ts +6 -0
  27. package/dist/data/coin-order.js +19 -0
  28. package/dist/data/coin-order.js.map +1 -0
  29. package/dist/data/drop-create-schema.d.cts +2 -2
  30. package/dist/data/drop-create-schema.d.ts +2 -2
  31. package/dist/index.cjs +3 -0
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +2 -1
  34. package/dist/index.d.ts +2 -1
  35. package/dist/index.js +2 -0
  36. package/dist/index.js.map +1 -1
  37. package/package.json +1 -1
@@ -1,58 +1,56 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useState, useMemo, useEffect } from "react";
4
- import { Search, SlidersHorizontal, X } from "lucide-react";
3
+ import { useState, useMemo } from "react";
4
+ import { Search } from "lucide-react";
5
5
  import { cn } from "../utils/cn.js";
6
6
  import { CoinRow, CoinRowSkeleton, COIN_GRID } from "./coin-row.js";
7
7
  import { StatPillRow } from "./stat-tile.js";
8
- import { coinKind, coinKindLabel, coinKindLabelPlural, coinServiceIds, COIN_KINDS } from "../data/coins.js";
9
- const FILTER_TABS = [
10
- { label: "All", value: "all" },
11
- ...COIN_KINDS.map((kind) => ({ label: coinKindLabel(kind), value: kind }))
12
- ];
8
+ import { orderCoins } from "../data/coin-order.js";
9
+ import { coinKind, coinKindLabelPlural, coinServiceIds, COIN_KINDS } from "../data/coins.js";
13
10
  const SORT_OPTIONS = [
14
11
  { label: "Recently launched", value: "recent" },
15
12
  { label: "Name", value: "name" }
16
13
  ];
17
- const filterLabel = (v) => FILTER_TABS.find((t) => t.value === v)?.label ?? "";
18
- const sortLabel = (v) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? "";
19
- function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true, action }) {
14
+ const noPriceMap = () => ({ prices: {}, isLoading: false });
15
+ function CoinsExplorer({ useCoins, usePrice, usePriceMap, coinHref, heading = true, action }) {
20
16
  const [filter, setFilter] = useState("all");
21
17
  const [sort, setSort] = useState("recent");
22
18
  const [query, setQuery] = useState("");
23
- const [filtersOpen, setFiltersOpen] = useState(false);
24
- const filterCount = (filter !== "all" ? 1 : 0) + (sort !== "recent" ? 1 : 0);
25
- useEffect(() => {
26
- if (!filtersOpen) return;
27
- const onKey = (e) => e.key === "Escape" && setFiltersOpen(false);
28
- window.addEventListener("keydown", onKey);
29
- return () => window.removeEventListener("keydown", onKey);
30
- }, [filtersOpen]);
19
+ const [priceSort, setPriceSort] = useState(void 0);
31
20
  const { collections, isLoading, counts } = useCoins({ filter, sort });
21
+ const { prices } = (usePriceMap ?? noPriceMap)();
32
22
  const items = useMemo(() => {
33
23
  const q = query.trim().toLowerCase();
34
- if (!q) return collections;
35
- return collections.filter(
36
- (c) => (c.name ?? "").toLowerCase().includes(q) || (c.symbol ?? "").toLowerCase().includes(q)
37
- );
38
- }, [collections, query]);
24
+ const matched = q ? collections.filter((c) => (c.name ?? "").toLowerCase().includes(q) || (c.symbol ?? "").toLowerCase().includes(q)) : collections;
25
+ return usePriceMap ? orderCoins(matched, prices, priceSort) : matched;
26
+ }, [collections, query, usePriceMap, prices, priceSort]);
39
27
  const showKind = useMemo(() => new Set(items.map((c) => coinKind(c.service))).size > 1, [items]);
40
- const statItems = useMemo(() => {
41
- if (!counts) return [];
28
+ const { statItems, pillFilters } = useMemo(() => {
29
+ if (!counts) return { statItems: [], pillFilters: [] };
42
30
  const forKind = (kind) => coinServiceIds(kind).reduce((sum, id) => sum + (counts[id] ?? 0), 0);
43
31
  const total = Object.values(counts).reduce((a, b) => a + b, 0);
44
- return [
45
- { label: total === 1 ? "Coin" : "Coins", value: total },
46
- ...COIN_KINDS.map((kind) => ({ label: coinKindLabelPlural(kind), value: forKind(kind) })).filter(
47
- (s) => s.value > 0
48
- )
49
- ];
32
+ const kinds = COIN_KINDS.filter((kind) => forKind(kind) > 0);
33
+ return {
34
+ statItems: [
35
+ { label: total === 1 ? "Coin" : "Coins", value: total },
36
+ ...kinds.map((kind) => ({ label: coinKindLabelPlural(kind), value: forKind(kind) }))
37
+ ],
38
+ pillFilters: ["all", ...kinds]
39
+ };
50
40
  }, [counts]);
41
+ const activeIndex = pillFilters.indexOf(filter);
51
42
  return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
52
43
  heading && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [
53
44
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
54
45
  /* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold", children: "Creator coins & memecoins" }),
55
- /* @__PURE__ */ jsx(StatPillRow, { items: statItems })
46
+ /* @__PURE__ */ jsx(
47
+ StatPillRow,
48
+ {
49
+ items: statItems,
50
+ onSelect: (index) => setFilter(pillFilters[index]),
51
+ activeIndex
52
+ }
53
+ )
56
54
  ] }),
57
55
  action
58
56
  ] }),
@@ -69,27 +67,32 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true, action })
69
67
  }
70
68
  )
71
69
  ] }),
72
- /* @__PURE__ */ jsxs(
73
- "button",
70
+ /* @__PURE__ */ jsx(
71
+ "select",
74
72
  {
75
- onClick: () => setFiltersOpen(true),
76
- 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",
77
- children: [
78
- /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
79
- "Filters",
80
- 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 })
81
- ]
73
+ value: sort,
74
+ onChange: (e) => setSort(e.target.value),
75
+ "aria-label": "Sort coins",
76
+ className: "shrink-0 rounded-lg border border-border bg-background px-2.5 py-2 text-xs font-medium text-foreground outline-none focus:border-primary/50",
77
+ children: SORT_OPTIONS.map(({ label, value }) => /* @__PURE__ */ jsx("option", { value, children: label }, value))
82
78
  }
83
79
  )
84
80
  ] }),
85
- filterCount > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
86
- filter !== "all" && /* @__PURE__ */ jsx(Chip, { onClear: () => setFilter("all"), children: filterLabel(filter) }),
87
- sort !== "recent" && /* @__PURE__ */ jsx(Chip, { onClear: () => setSort("recent"), children: sortLabel(sort) })
88
- ] }),
89
81
  /* @__PURE__ */ jsxs("div", { children: [
90
82
  /* @__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: [
91
83
  /* @__PURE__ */ jsx("span", { children: "Token" }),
92
- /* @__PURE__ */ jsx("span", { className: "text-right", children: "Price" })
84
+ usePriceMap ? /* @__PURE__ */ jsxs(
85
+ "button",
86
+ {
87
+ type: "button",
88
+ onClick: () => setPriceSort(priceSort === void 0 ? "desc" : priceSort === "desc" ? "asc" : void 0),
89
+ className: cn("text-right uppercase tracking-wide", priceSort ? "text-foreground" : "text-muted-foreground"),
90
+ children: [
91
+ "Price",
92
+ priceSort === "desc" ? " \u2193" : priceSort === "asc" ? " \u2191" : ""
93
+ ]
94
+ }
95
+ ) : /* @__PURE__ */ jsx("span", { className: "text-right", children: "Price" })
93
96
  ] }),
94
97
  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(
95
98
  CoinRow,
@@ -101,71 +104,9 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true, action })
101
104
  },
102
105
  `${c.chain}-${c.contractAddress}`
103
106
  ))
104
- ] }),
105
- 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: [
106
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-background/70 backdrop-blur-sm", onClick: () => setFiltersOpen(false) }),
107
- /* @__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: [
108
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
109
- /* @__PURE__ */ jsxs("h2", { className: "flex items-center gap-2 text-base font-bold", children: [
110
- /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-4 w-4 text-primary" }),
111
- "Filters"
112
- ] }),
113
- filterCount > 0 && /* @__PURE__ */ jsx(
114
- "button",
115
- {
116
- onClick: () => {
117
- setFilter("all");
118
- setSort("recent");
119
- },
120
- className: "text-xs font-medium text-muted-foreground hover:text-foreground",
121
- children: "Clear all"
122
- }
123
- )
124
- ] }),
125
- /* @__PURE__ */ jsx(FilterGroup, { label: "Type", children: FILTER_TABS.map(({ label, value }) => /* @__PURE__ */ jsx(PillButton, { active: filter === value, onClick: () => setFilter(value), children: label }, value)) }),
126
- /* @__PURE__ */ jsx(FilterGroup, { label: "Sort", children: SORT_OPTIONS.map(({ label, value }) => /* @__PURE__ */ jsx(PillButton, { active: sort === value, onClick: () => setSort(value), children: label }, value)) }),
127
- /* @__PURE__ */ jsxs(
128
- "button",
129
- {
130
- onClick: () => setFiltersOpen(false),
131
- className: "w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white",
132
- children: [
133
- "Show ",
134
- items.length,
135
- " ",
136
- items.length === 1 ? "coin" : "coins"
137
- ]
138
- }
139
- )
140
- ] })
141
107
  ] })
142
108
  ] });
143
109
  }
144
- function FilterGroup({ label, children }) {
145
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
146
- /* @__PURE__ */ jsx("p", { className: "text-2xs font-medium text-muted-foreground", children: label }),
147
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children })
148
- ] });
149
- }
150
- function PillButton({ active, onClick, children }) {
151
- return /* @__PURE__ */ jsx(
152
- "button",
153
- {
154
- onClick,
155
- className: cn(
156
- "rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
157
- active ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
158
- ),
159
- children
160
- }
161
- );
162
- }
163
- function Chip({ children, onClear }) {
164
- return /* @__PURE__ */ jsxs("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", children: [
165
- children,
166
- /* @__PURE__ */ jsx("button", { onClick: onClear, "aria-label": "Clear filter", className: "hover:text-primary/60", children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }) })
167
- ] });
168
- }
169
110
  export {
170
111
  CoinsExplorer
171
112
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinRow, CoinRowSkeleton, COIN_GRID, type UseCoinPrice } from \"./coin-row.js\";\nimport { StatPillRow } from \"./stat-tile.js\";\nimport { coinKind, coinKindLabel, coinKindLabelPlural, coinServiceIds, COIN_KINDS, type CoinCollectionLike, type CoinKind } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | CoinKind;\nexport type CoinSort = \"recent\" | \"name\";\nexport type CoinCounts = Record<string, number>;\n\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n counts?: CoinCounts;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n\n action?: React.ReactNode;\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, action }: 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, counts } = 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 const statItems = useMemo(() => {\n if (!counts) return [];\n const forKind = (kind: CoinKind) =>\n coinServiceIds(kind).reduce((sum, id) => sum + (counts[id] ?? 0), 0);\n const total = Object.values(counts).reduce((a, b) => a + b, 0);\n return [\n { label: total === 1 ? \"Coin\" : \"Coins\", value: total },\n ...COIN_KINDS.map((kind) => ({ label: coinKindLabelPlural(kind), value: forKind(kind) })).filter(\n (s) => s.value > 0,\n ),\n ];\n }, [counts]);\n\n return (\n <div className=\"space-y-5\">\n {heading && (\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold\">Creator coins &amp; memecoins</h1>\n <StatPillRow items={statItems} />\n </div>\n {action}\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 } from \"react\";\nimport { Search } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinRow, CoinRowSkeleton, COIN_GRID, type UseCoinPrice } from \"./coin-row.js\";\nimport { StatPillRow } from \"./stat-tile.js\";\nimport { orderCoins, type CoinPriceSort } from \"../data/coin-order.js\";\nimport { coinKind, coinKindLabelPlural, coinServiceIds, COIN_KINDS, type CoinCollectionLike, type CoinKind } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | CoinKind;\nexport type CoinSort = \"recent\" | \"name\";\nexport type CoinCounts = Record<string, number>;\n\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n counts?: CoinCounts;\n};\n\nexport type UsePriceMap = () => { prices: Record<string, number | null>; isLoading: boolean };\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n usePriceMap?: UsePriceMap;\n\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n\n action?: React.ReactNode;\n}\n\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst noPriceMap: UsePriceMap = () => ({ prices: {}, isLoading: false });\n\nexport function CoinsExplorer({ useCoins, usePrice, usePriceMap, coinHref, heading = true, action }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [query, setQuery] = useState(\"\");\n const [priceSort, setPriceSort] = useState<CoinPriceSort | undefined>(undefined);\n\n const { collections, isLoading, counts } = useCoins({ filter, sort });\n const { prices } = (usePriceMap ?? noPriceMap)();\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n const matched = q\n ? collections.filter((c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q))\n : collections;\n return usePriceMap ? orderCoins(matched, prices, priceSort) : matched;\n }, [collections, query, usePriceMap, prices, priceSort]);\n\n const showKind = useMemo(() => new Set(items.map((c) => coinKind(c.service))).size > 1, [items]);\n\n const { statItems, pillFilters } = useMemo(() => {\n if (!counts) return { statItems: [], pillFilters: [] as CoinFilter[] };\n const forKind = (kind: CoinKind) =>\n coinServiceIds(kind).reduce((sum, id) => sum + (counts[id] ?? 0), 0);\n const total = Object.values(counts).reduce((a, b) => a + b, 0);\n const kinds = COIN_KINDS.filter((kind) => forKind(kind) > 0);\n return {\n statItems: [\n { label: total === 1 ? \"Coin\" : \"Coins\", value: total },\n ...kinds.map((kind) => ({ label: coinKindLabelPlural(kind), value: forKind(kind) })),\n ],\n pillFilters: [\"all\", ...kinds] as CoinFilter[],\n };\n }, [counts]);\n\n const activeIndex = pillFilters.indexOf(filter);\n\n return (\n <div className=\"space-y-5\">\n {heading && (\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold\">Creator coins &amp; memecoins</h1>\n <StatPillRow\n items={statItems}\n onSelect={(index) => setFilter(pillFilters[index])}\n activeIndex={activeIndex}\n />\n </div>\n {action}\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 <select\n value={sort}\n onChange={(e) => setSort(e.target.value as CoinSort)}\n aria-label=\"Sort coins\"\n className=\"shrink-0 rounded-lg border border-border bg-background px-2.5 py-2 text-xs font-medium text-foreground outline-none focus:border-primary/50\"\n >\n {SORT_OPTIONS.map(({ label, value }) => (\n <option key={value} value={value}>{label}</option>\n ))}\n </select>\n </div>\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 {usePriceMap ? (\n <button\n type=\"button\"\n onClick={() => setPriceSort(priceSort === undefined ? \"desc\" : priceSort === \"desc\" ? \"asc\" : undefined)}\n className={cn(\"text-right uppercase tracking-wide\", priceSort ? \"text-foreground\" : \"text-muted-foreground\")}\n >\n Price{priceSort === \"desc\" ? \" ↓\" : priceSort === \"asc\" ? \" ↑\" : \"\"}\n </button>\n ) : (\n <span className=\"text-right\">Price</span>\n )}\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 </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -74,7 +74,8 @@ function DualPrice({
74
74
  currency,
75
75
  usdValue,
76
76
  scale,
77
- trailing
77
+ trailing,
78
+ secondaryClassName
78
79
  }) {
79
80
  if (!amountFormatted) return null;
80
81
  const s = DUAL_PRICE_SCALES[scale];
@@ -105,19 +106,21 @@ function DualPrice({
105
106
  amountClassName: amountClass
106
107
  }
107
108
  ),
108
- !stable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `${s.divider} w-px bg-border/60 shrink-0` }),
109
- stable ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "inline-flex items-center gap-1.5", children: [
110
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CoinChip, { symbol: currency, size: s.stableChip }),
111
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-semibold text-muted-foreground", children: currency })
112
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
113
- PriceUnit,
114
- {
115
- chip: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CoinChip, { symbol: currency, size: s.chip }),
116
- amount: cryptoDisplay,
117
- label: s.showLabel ? currency ?? void 0 : void 0,
118
- amountClassName: amountClass
119
- }
120
- ),
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: `items-center gap-2.5 ${secondaryClassName ?? "flex"}`, children: [
110
+ !stable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `${s.divider} w-px bg-border/60 shrink-0` }),
111
+ stable ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "inline-flex items-center gap-1.5", children: [
112
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CoinChip, { symbol: currency, size: s.stableChip }),
113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-semibold text-muted-foreground", children: currency })
114
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
115
+ PriceUnit,
116
+ {
117
+ chip: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CoinChip, { symbol: currency, size: s.chip }),
118
+ amount: cryptoDisplay,
119
+ label: s.showLabel ? currency ?? void 0 : void 0,
120
+ amountClassName: amountClass
121
+ }
122
+ )
123
+ ] }),
121
124
  trailing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ml-0.5", children: trailing })
122
125
  ] });
123
126
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/dual-price.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\n\nexport function CoinChip({ symbol, size }: { symbol: string | null | undefined; size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06]\"\n style={{ width: size + 10, height: size + 10 }}\n >\n <CurrencyIcon symbol={symbol ?? \"\"} size={size} />\n </span>\n );\n}\n\nfunction FiatChip({ size }: { size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06] font-extrabold leading-none text-foreground/80\"\n style={{ width: size + 10, height: size + 10, fontSize: size }}\n >\n $\n </span>\n );\n}\n\nfunction stripDollarSign(usdValue: string): string {\n return usdValue.replace(\"$\", \"\");\n}\n\nconst DISPLAY_FACE = \"font-extrabold tracking-tight tabular-nums\";\n\nconst DUAL_PRICE_SCALES = {\n\n hero: { amount: \"text-3xl\", chip: 18, stableChip: 14, divider: \"h-7\", showLabel: true },\n\n card: { amount: \"text-base\", chip: 13, stableChip: 10, divider: \"h-4\", showLabel: false },\n} as const;\n\nfunction PriceUnit({\n chip,\n amount,\n label,\n amountClassName,\n}: {\n chip: ReactNode;\n amount: string;\n label?: string;\n amountClassName: string;\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5\">\n {chip}\n <span className=\"flex items-baseline gap-1.5\">\n <span className={amountClassName}>{amount}</span>\n {label && <span className=\"text-sm font-semibold text-muted-foreground/70\">{label}</span>}\n </span>\n </span>\n );\n}\n\nexport function DualPrice({\n amountFormatted,\n currency,\n usdValue,\n scale,\n trailing,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n scale: keyof typeof DUAL_PRICE_SCALES;\n trailing?: ReactNode;\n}) {\n if (!amountFormatted) return null;\n const s = DUAL_PRICE_SCALES[scale];\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const amountClass = `${DISPLAY_FACE} ${s.amount}`;\n\n if (!usdValue) {\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n }\n\n const stable = isStableCurrency(currency);\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<FiatChip size={s.chip} />}\n amount={stripDollarSign(usdValue)}\n label={s.showLabel ? \"USD\" : undefined}\n amountClassName={amountClass}\n />\n {!stable && <span className={`${s.divider} w-px bg-border/60 shrink-0`} />}\n {stable ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CoinChip symbol={currency} size={s.stableChip} />\n <span className=\"text-sm font-semibold text-muted-foreground\">{currency}</span>\n </span>\n ) : (\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n )}\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n}\n\nconst CHIP_TONES = {\n\n \"overlay-dark\": { dot: \"text-white/30\", text: \"text-white/65\" },\n\n card: { dot: \"text-muted-foreground/50\", text: \"text-muted-foreground\" },\n\n /** Same solid color as the primary USD figure — for cards that want a\n * single consistent shade instead of a muted secondary currency. */\n solid: { dot: \"text-foreground/40\", text: \"text-foreground\" },\n} as const;\n\nexport function PriceChipContent({\n amountFormatted,\n currency,\n usdValue,\n tone,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n tone: keyof typeof CHIP_TONES;\n}) {\n if (!amountFormatted) return null;\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const stable = isStableCurrency(currency);\n\n if (!usdValue) {\n return (\n <>\n {currency && <CurrencyIcon symbol={currency} size={13} />}\n {cryptoDisplay}\n </>\n );\n }\n\n const t = CHIP_TONES[tone];\n return (\n <>\n {stripDollarSign(usdValue)}\n <span className={`font-semibold ${t.text}`}>USD</span>\n <span className={t.dot}>·</span>\n <span className={`inline-flex items-center gap-1 font-semibold ${t.text}`}>\n {currency && <CurrencyIcon symbol={currency} size={12} />}\n {stable ? currency : cryptoDisplay}\n </span>\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYM;AATN,2BAA6B;AAC7B,oBAAqD;AAE9C,SAAS,SAAS,EAAE,QAAQ,KAAK,GAAwD;AAC9F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,OAAO,OAAO,IAAI,QAAQ,OAAO,GAAG;AAAA,MAE7C,sDAAC,qCAAa,QAAQ,UAAU,IAAI,MAAY;AAAA;AAAA,EAClD;AAEJ;AAEA,SAAS,SAAS,EAAE,KAAK,GAAqB;AAC5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,OAAO,OAAO,IAAI,QAAQ,OAAO,IAAI,UAAU,KAAK;AAAA,MAC9D;AAAA;AAAA,EAED;AAEJ;AAEA,SAAS,gBAAgB,UAA0B;AACjD,SAAO,SAAS,QAAQ,KAAK,EAAE;AACjC;AAEA,MAAM,eAAe;AAErB,MAAM,oBAAoB;AAAA,EAExB,MAAM,EAAE,QAAQ,YAAY,MAAM,IAAI,YAAY,IAAI,SAAS,OAAO,WAAW,KAAK;AAAA,EAEtF,MAAM,EAAE,QAAQ,aAAa,MAAM,IAAI,YAAY,IAAI,SAAS,OAAO,WAAW,MAAM;AAC1F;AAEA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,6CAAC,UAAK,WAAU,oCACb;AAAA;AAAA,IACD,6CAAC,UAAK,WAAU,+BACd;AAAA,kDAAC,UAAK,WAAW,iBAAkB,kBAAO;AAAA,MACzC,SAAS,4CAAC,UAAK,WAAU,kDAAkD,iBAAM;AAAA,OACpF;AAAA,KACF;AAEJ;AAEO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,MAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAM,IAAI,kBAAkB,KAAK;AACjC,QAAM,oBAAgB,kCAAmB,eAAe;AACxD,QAAM,cAAc,GAAG,YAAY,IAAI,EAAE,MAAM;AAE/C,MAAI,CAAC,UAAU;AACb,WACE,6CAAC,SAAI,WAAU,6BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,4CAAC,YAAS,QAAQ,UAAU,MAAM,EAAE,MAAM;AAAA,UAChD,QAAQ;AAAA,UACR,OAAO,EAAE,YAAa,YAAY,SAAa;AAAA,UAC/C,iBAAiB;AAAA;AAAA,MACnB;AAAA,MACC,YAAY,4CAAC,UAAK,WAAU,UAAU,oBAAS;AAAA,OAClD;AAAA,EAEJ;AAEA,QAAM,aAAS,gCAAiB,QAAQ;AACxC,SACE,6CAAC,SAAI,WAAU,6BACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,4CAAC,YAAS,MAAM,EAAE,MAAM;AAAA,QAC9B,QAAQ,gBAAgB,QAAQ;AAAA,QAChC,OAAO,EAAE,YAAY,QAAQ;AAAA,QAC7B,iBAAiB;AAAA;AAAA,IACnB;AAAA,IACC,CAAC,UAAU,4CAAC,UAAK,WAAW,GAAG,EAAE,OAAO,+BAA+B;AAAA,IACvE,SACC,6CAAC,UAAK,WAAU,oCACd;AAAA,kDAAC,YAAS,QAAQ,UAAU,MAAM,EAAE,YAAY;AAAA,MAChD,4CAAC,UAAK,WAAU,+CAA+C,oBAAS;AAAA,OAC1E,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,4CAAC,YAAS,QAAQ,UAAU,MAAM,EAAE,MAAM;AAAA,QAChD,QAAQ;AAAA,QACR,OAAO,EAAE,YAAa,YAAY,SAAa;AAAA,QAC/C,iBAAiB;AAAA;AAAA,IACnB;AAAA,IAED,YAAY,4CAAC,UAAK,WAAU,UAAU,oBAAS;AAAA,KAClD;AAEJ;AAEA,MAAM,aAAa;AAAA,EAEjB,gBAAgB,EAAE,KAAK,iBAAiB,MAAM,gBAAgB;AAAA,EAE9D,MAAM,EAAE,KAAK,4BAA4B,MAAM,wBAAwB;AAAA;AAAA;AAAA,EAIvE,OAAO,EAAE,KAAK,sBAAsB,MAAM,kBAAkB;AAC9D;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAM,oBAAgB,kCAAmB,eAAe;AACxD,QAAM,aAAS,gCAAiB,QAAQ;AAExC,MAAI,CAAC,UAAU;AACb,WACE,4EACG;AAAA,kBAAY,4CAAC,qCAAa,QAAQ,UAAU,MAAM,IAAI;AAAA,MACtD;AAAA,OACH;AAAA,EAEJ;AAEA,QAAM,IAAI,WAAW,IAAI;AACzB,SACE,4EACG;AAAA,oBAAgB,QAAQ;AAAA,IACzB,4CAAC,UAAK,WAAW,iBAAiB,EAAE,IAAI,IAAI,iBAAG;AAAA,IAC/C,4CAAC,UAAK,WAAW,EAAE,KAAK,kBAAC;AAAA,IACzB,6CAAC,UAAK,WAAW,gDAAgD,EAAE,IAAI,IACpE;AAAA,kBAAY,4CAAC,qCAAa,QAAQ,UAAU,MAAM,IAAI;AAAA,MACtD,SAAS,WAAW;AAAA,OACvB;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/dual-price.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\n\nexport function CoinChip({ symbol, size }: { symbol: string | null | undefined; size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06]\"\n style={{ width: size + 10, height: size + 10 }}\n >\n <CurrencyIcon symbol={symbol ?? \"\"} size={size} />\n </span>\n );\n}\n\nfunction FiatChip({ size }: { size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06] font-extrabold leading-none text-foreground/80\"\n style={{ width: size + 10, height: size + 10, fontSize: size }}\n >\n $\n </span>\n );\n}\n\nfunction stripDollarSign(usdValue: string): string {\n return usdValue.replace(\"$\", \"\");\n}\n\nconst DISPLAY_FACE = \"font-extrabold tracking-tight tabular-nums\";\n\nconst DUAL_PRICE_SCALES = {\n\n hero: { amount: \"text-3xl\", chip: 18, stableChip: 14, divider: \"h-7\", showLabel: true },\n\n card: { amount: \"text-base\", chip: 13, stableChip: 10, divider: \"h-4\", showLabel: false },\n} as const;\n\nfunction PriceUnit({\n chip,\n amount,\n label,\n amountClassName,\n}: {\n chip: ReactNode;\n amount: string;\n label?: string;\n amountClassName: string;\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5\">\n {chip}\n <span className=\"flex items-baseline gap-1.5\">\n <span className={amountClassName}>{amount}</span>\n {label && <span className=\"text-sm font-semibold text-muted-foreground/70\">{label}</span>}\n </span>\n </span>\n );\n}\n\nexport function DualPrice({\n amountFormatted,\n currency,\n usdValue,\n scale,\n trailing,\n secondaryClassName,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n scale: keyof typeof DUAL_PRICE_SCALES;\n trailing?: ReactNode;\n secondaryClassName?: string;\n}) {\n if (!amountFormatted) return null;\n const s = DUAL_PRICE_SCALES[scale];\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const amountClass = `${DISPLAY_FACE} ${s.amount}`;\n\n if (!usdValue) {\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n }\n\n const stable = isStableCurrency(currency);\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<FiatChip size={s.chip} />}\n amount={stripDollarSign(usdValue)}\n label={s.showLabel ? \"USD\" : undefined}\n amountClassName={amountClass}\n />\n <span className={`items-center gap-2.5 ${secondaryClassName ?? \"flex\"}`}>\n {!stable && <span className={`${s.divider} w-px bg-border/60 shrink-0`} />}\n {stable ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CoinChip symbol={currency} size={s.stableChip} />\n <span className=\"text-sm font-semibold text-muted-foreground\">{currency}</span>\n </span>\n ) : (\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n )}\n </span>\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n}\n\nconst CHIP_TONES = {\n\n \"overlay-dark\": { dot: \"text-white/30\", text: \"text-white/65\" },\n\n card: { dot: \"text-muted-foreground/50\", text: \"text-muted-foreground\" },\n\n /** Same solid color as the primary USD figure — for cards that want a\n * single consistent shade instead of a muted secondary currency. */\n solid: { dot: \"text-foreground/40\", text: \"text-foreground\" },\n} as const;\n\nexport function PriceChipContent({\n amountFormatted,\n currency,\n usdValue,\n tone,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n tone: keyof typeof CHIP_TONES;\n}) {\n if (!amountFormatted) return null;\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const stable = isStableCurrency(currency);\n\n if (!usdValue) {\n return (\n <>\n {currency && <CurrencyIcon symbol={currency} size={13} />}\n {cryptoDisplay}\n </>\n );\n }\n\n const t = CHIP_TONES[tone];\n return (\n <>\n {stripDollarSign(usdValue)}\n <span className={`font-semibold ${t.text}`}>USD</span>\n <span className={t.dot}>·</span>\n <span className={`inline-flex items-center gap-1 font-semibold ${t.text}`}>\n {currency && <CurrencyIcon symbol={currency} size={12} />}\n {stable ? currency : cryptoDisplay}\n </span>\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYM;AATN,2BAA6B;AAC7B,oBAAqD;AAE9C,SAAS,SAAS,EAAE,QAAQ,KAAK,GAAwD;AAC9F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,OAAO,OAAO,IAAI,QAAQ,OAAO,GAAG;AAAA,MAE7C,sDAAC,qCAAa,QAAQ,UAAU,IAAI,MAAY;AAAA;AAAA,EAClD;AAEJ;AAEA,SAAS,SAAS,EAAE,KAAK,GAAqB;AAC5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,OAAO,OAAO,IAAI,QAAQ,OAAO,IAAI,UAAU,KAAK;AAAA,MAC9D;AAAA;AAAA,EAED;AAEJ;AAEA,SAAS,gBAAgB,UAA0B;AACjD,SAAO,SAAS,QAAQ,KAAK,EAAE;AACjC;AAEA,MAAM,eAAe;AAErB,MAAM,oBAAoB;AAAA,EAExB,MAAM,EAAE,QAAQ,YAAY,MAAM,IAAI,YAAY,IAAI,SAAS,OAAO,WAAW,KAAK;AAAA,EAEtF,MAAM,EAAE,QAAQ,aAAa,MAAM,IAAI,YAAY,IAAI,SAAS,OAAO,WAAW,MAAM;AAC1F;AAEA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,6CAAC,UAAK,WAAU,oCACb;AAAA;AAAA,IACD,6CAAC,UAAK,WAAU,+BACd;AAAA,kDAAC,UAAK,WAAW,iBAAkB,kBAAO;AAAA,MACzC,SAAS,4CAAC,UAAK,WAAU,kDAAkD,iBAAM;AAAA,OACpF;AAAA,KACF;AAEJ;AAEO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,MAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAM,IAAI,kBAAkB,KAAK;AACjC,QAAM,oBAAgB,kCAAmB,eAAe;AACxD,QAAM,cAAc,GAAG,YAAY,IAAI,EAAE,MAAM;AAE/C,MAAI,CAAC,UAAU;AACb,WACE,6CAAC,SAAI,WAAU,6BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,4CAAC,YAAS,QAAQ,UAAU,MAAM,EAAE,MAAM;AAAA,UAChD,QAAQ;AAAA,UACR,OAAO,EAAE,YAAa,YAAY,SAAa;AAAA,UAC/C,iBAAiB;AAAA;AAAA,MACnB;AAAA,MACC,YAAY,4CAAC,UAAK,WAAU,UAAU,oBAAS;AAAA,OAClD;AAAA,EAEJ;AAEA,QAAM,aAAS,gCAAiB,QAAQ;AACxC,SACE,6CAAC,SAAI,WAAU,6BACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,4CAAC,YAAS,MAAM,EAAE,MAAM;AAAA,QAC9B,QAAQ,gBAAgB,QAAQ;AAAA,QAChC,OAAO,EAAE,YAAY,QAAQ;AAAA,QAC7B,iBAAiB;AAAA;AAAA,IACnB;AAAA,IACA,6CAAC,UAAK,WAAW,wBAAwB,sBAAsB,MAAM,IAClE;AAAA,OAAC,UAAU,4CAAC,UAAK,WAAW,GAAG,EAAE,OAAO,+BAA+B;AAAA,MACvE,SACC,6CAAC,UAAK,WAAU,oCACd;AAAA,oDAAC,YAAS,QAAQ,UAAU,MAAM,EAAE,YAAY;AAAA,QAChD,4CAAC,UAAK,WAAU,+CAA+C,oBAAS;AAAA,SAC1E,IAEA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,4CAAC,YAAS,QAAQ,UAAU,MAAM,EAAE,MAAM;AAAA,UAChD,QAAQ;AAAA,UACR,OAAO,EAAE,YAAa,YAAY,SAAa;AAAA,UAC/C,iBAAiB;AAAA;AAAA,MACnB;AAAA,OAEJ;AAAA,IACC,YAAY,4CAAC,UAAK,WAAU,UAAU,oBAAS;AAAA,KAClD;AAEJ;AAEA,MAAM,aAAa;AAAA,EAEjB,gBAAgB,EAAE,KAAK,iBAAiB,MAAM,gBAAgB;AAAA,EAE9D,MAAM,EAAE,KAAK,4BAA4B,MAAM,wBAAwB;AAAA;AAAA;AAAA,EAIvE,OAAO,EAAE,KAAK,sBAAsB,MAAM,kBAAkB;AAC9D;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAM,oBAAgB,kCAAmB,eAAe;AACxD,QAAM,aAAS,gCAAiB,QAAQ;AAExC,MAAI,CAAC,UAAU;AACb,WACE,4EACG;AAAA,kBAAY,4CAAC,qCAAa,QAAQ,UAAU,MAAM,IAAI;AAAA,MACtD;AAAA,OACH;AAAA,EAEJ;AAEA,QAAM,IAAI,WAAW,IAAI;AACzB,SACE,4EACG;AAAA,oBAAgB,QAAQ;AAAA,IACzB,4CAAC,UAAK,WAAW,iBAAiB,EAAE,IAAI,IAAI,iBAAG;AAAA,IAC/C,4CAAC,UAAK,WAAW,EAAE,KAAK,kBAAC;AAAA,IACzB,6CAAC,UAAK,WAAW,gDAAgD,EAAE,IAAI,IACpE;AAAA,kBAAY,4CAAC,qCAAa,QAAQ,UAAU,MAAM,IAAI;AAAA,MACtD,SAAS,WAAW;AAAA,OACvB;AAAA,KACF;AAEJ;","names":[]}
@@ -21,12 +21,13 @@ declare const DUAL_PRICE_SCALES: {
21
21
  readonly showLabel: false;
22
22
  };
23
23
  };
24
- declare function DualPrice({ amountFormatted, currency, usdValue, scale, trailing, }: {
24
+ declare function DualPrice({ amountFormatted, currency, usdValue, scale, trailing, secondaryClassName, }: {
25
25
  amountFormatted: string | null | undefined;
26
26
  currency: string | null | undefined;
27
27
  usdValue?: string | null;
28
28
  scale: keyof typeof DUAL_PRICE_SCALES;
29
29
  trailing?: ReactNode;
30
+ secondaryClassName?: string;
30
31
  }): react_jsx_runtime.JSX.Element | null;
31
32
  declare const CHIP_TONES: {
32
33
  readonly "overlay-dark": {
@@ -21,12 +21,13 @@ declare const DUAL_PRICE_SCALES: {
21
21
  readonly showLabel: false;
22
22
  };
23
23
  };
24
- declare function DualPrice({ amountFormatted, currency, usdValue, scale, trailing, }: {
24
+ declare function DualPrice({ amountFormatted, currency, usdValue, scale, trailing, secondaryClassName, }: {
25
25
  amountFormatted: string | null | undefined;
26
26
  currency: string | null | undefined;
27
27
  usdValue?: string | null;
28
28
  scale: keyof typeof DUAL_PRICE_SCALES;
29
29
  trailing?: ReactNode;
30
+ secondaryClassName?: string;
30
31
  }): react_jsx_runtime.JSX.Element | null;
31
32
  declare const CHIP_TONES: {
32
33
  readonly "overlay-dark": {
@@ -49,7 +49,8 @@ function DualPrice({
49
49
  currency,
50
50
  usdValue,
51
51
  scale,
52
- trailing
52
+ trailing,
53
+ secondaryClassName
53
54
  }) {
54
55
  if (!amountFormatted) return null;
55
56
  const s = DUAL_PRICE_SCALES[scale];
@@ -80,19 +81,21 @@ function DualPrice({
80
81
  amountClassName: amountClass
81
82
  }
82
83
  ),
83
- !stable && /* @__PURE__ */ jsx("span", { className: `${s.divider} w-px bg-border/60 shrink-0` }),
84
- stable ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
85
- /* @__PURE__ */ jsx(CoinChip, { symbol: currency, size: s.stableChip }),
86
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-muted-foreground", children: currency })
87
- ] }) : /* @__PURE__ */ jsx(
88
- PriceUnit,
89
- {
90
- chip: /* @__PURE__ */ jsx(CoinChip, { symbol: currency, size: s.chip }),
91
- amount: cryptoDisplay,
92
- label: s.showLabel ? currency ?? void 0 : void 0,
93
- amountClassName: amountClass
94
- }
95
- ),
84
+ /* @__PURE__ */ jsxs("span", { className: `items-center gap-2.5 ${secondaryClassName ?? "flex"}`, children: [
85
+ !stable && /* @__PURE__ */ jsx("span", { className: `${s.divider} w-px bg-border/60 shrink-0` }),
86
+ stable ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
87
+ /* @__PURE__ */ jsx(CoinChip, { symbol: currency, size: s.stableChip }),
88
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-muted-foreground", children: currency })
89
+ ] }) : /* @__PURE__ */ jsx(
90
+ PriceUnit,
91
+ {
92
+ chip: /* @__PURE__ */ jsx(CoinChip, { symbol: currency, size: s.chip }),
93
+ amount: cryptoDisplay,
94
+ label: s.showLabel ? currency ?? void 0 : void 0,
95
+ amountClassName: amountClass
96
+ }
97
+ )
98
+ ] }),
96
99
  trailing && /* @__PURE__ */ jsx("span", { className: "ml-0.5", children: trailing })
97
100
  ] });
98
101
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/dual-price.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\n\nexport function CoinChip({ symbol, size }: { symbol: string | null | undefined; size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06]\"\n style={{ width: size + 10, height: size + 10 }}\n >\n <CurrencyIcon symbol={symbol ?? \"\"} size={size} />\n </span>\n );\n}\n\nfunction FiatChip({ size }: { size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06] font-extrabold leading-none text-foreground/80\"\n style={{ width: size + 10, height: size + 10, fontSize: size }}\n >\n $\n </span>\n );\n}\n\nfunction stripDollarSign(usdValue: string): string {\n return usdValue.replace(\"$\", \"\");\n}\n\nconst DISPLAY_FACE = \"font-extrabold tracking-tight tabular-nums\";\n\nconst DUAL_PRICE_SCALES = {\n\n hero: { amount: \"text-3xl\", chip: 18, stableChip: 14, divider: \"h-7\", showLabel: true },\n\n card: { amount: \"text-base\", chip: 13, stableChip: 10, divider: \"h-4\", showLabel: false },\n} as const;\n\nfunction PriceUnit({\n chip,\n amount,\n label,\n amountClassName,\n}: {\n chip: ReactNode;\n amount: string;\n label?: string;\n amountClassName: string;\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5\">\n {chip}\n <span className=\"flex items-baseline gap-1.5\">\n <span className={amountClassName}>{amount}</span>\n {label && <span className=\"text-sm font-semibold text-muted-foreground/70\">{label}</span>}\n </span>\n </span>\n );\n}\n\nexport function DualPrice({\n amountFormatted,\n currency,\n usdValue,\n scale,\n trailing,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n scale: keyof typeof DUAL_PRICE_SCALES;\n trailing?: ReactNode;\n}) {\n if (!amountFormatted) return null;\n const s = DUAL_PRICE_SCALES[scale];\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const amountClass = `${DISPLAY_FACE} ${s.amount}`;\n\n if (!usdValue) {\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n }\n\n const stable = isStableCurrency(currency);\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<FiatChip size={s.chip} />}\n amount={stripDollarSign(usdValue)}\n label={s.showLabel ? \"USD\" : undefined}\n amountClassName={amountClass}\n />\n {!stable && <span className={`${s.divider} w-px bg-border/60 shrink-0`} />}\n {stable ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CoinChip symbol={currency} size={s.stableChip} />\n <span className=\"text-sm font-semibold text-muted-foreground\">{currency}</span>\n </span>\n ) : (\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n )}\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n}\n\nconst CHIP_TONES = {\n\n \"overlay-dark\": { dot: \"text-white/30\", text: \"text-white/65\" },\n\n card: { dot: \"text-muted-foreground/50\", text: \"text-muted-foreground\" },\n\n /** Same solid color as the primary USD figure — for cards that want a\n * single consistent shade instead of a muted secondary currency. */\n solid: { dot: \"text-foreground/40\", text: \"text-foreground\" },\n} as const;\n\nexport function PriceChipContent({\n amountFormatted,\n currency,\n usdValue,\n tone,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n tone: keyof typeof CHIP_TONES;\n}) {\n if (!amountFormatted) return null;\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const stable = isStableCurrency(currency);\n\n if (!usdValue) {\n return (\n <>\n {currency && <CurrencyIcon symbol={currency} size={13} />}\n {cryptoDisplay}\n </>\n );\n }\n\n const t = CHIP_TONES[tone];\n return (\n <>\n {stripDollarSign(usdValue)}\n <span className={`font-semibold ${t.text}`}>USD</span>\n <span className={t.dot}>·</span>\n <span className={`inline-flex items-center gap-1 font-semibold ${t.text}`}>\n {currency && <CurrencyIcon symbol={currency} size={12} />}\n {stable ? currency : cryptoDisplay}\n </span>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/dual-price.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\n\nexport function CoinChip({ symbol, size }: { symbol: string | null | undefined; size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06]\"\n style={{ width: size + 10, height: size + 10 }}\n >\n <CurrencyIcon symbol={symbol ?? \"\"} size={size} />\n </span>\n );\n}\n\nfunction FiatChip({ size }: { size: number }) {\n return (\n <span\n className=\"grid shrink-0 place-items-center rounded-full bg-foreground/[0.06] font-extrabold leading-none text-foreground/80\"\n style={{ width: size + 10, height: size + 10, fontSize: size }}\n >\n $\n </span>\n );\n}\n\nfunction stripDollarSign(usdValue: string): string {\n return usdValue.replace(\"$\", \"\");\n}\n\nconst DISPLAY_FACE = \"font-extrabold tracking-tight tabular-nums\";\n\nconst DUAL_PRICE_SCALES = {\n\n hero: { amount: \"text-3xl\", chip: 18, stableChip: 14, divider: \"h-7\", showLabel: true },\n\n card: { amount: \"text-base\", chip: 13, stableChip: 10, divider: \"h-4\", showLabel: false },\n} as const;\n\nfunction PriceUnit({\n chip,\n amount,\n label,\n amountClassName,\n}: {\n chip: ReactNode;\n amount: string;\n label?: string;\n amountClassName: string;\n}) {\n return (\n <span className=\"inline-flex items-center gap-1.5\">\n {chip}\n <span className=\"flex items-baseline gap-1.5\">\n <span className={amountClassName}>{amount}</span>\n {label && <span className=\"text-sm font-semibold text-muted-foreground/70\">{label}</span>}\n </span>\n </span>\n );\n}\n\nexport function DualPrice({\n amountFormatted,\n currency,\n usdValue,\n scale,\n trailing,\n secondaryClassName,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n scale: keyof typeof DUAL_PRICE_SCALES;\n trailing?: ReactNode;\n secondaryClassName?: string;\n}) {\n if (!amountFormatted) return null;\n const s = DUAL_PRICE_SCALES[scale];\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const amountClass = `${DISPLAY_FACE} ${s.amount}`;\n\n if (!usdValue) {\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n }\n\n const stable = isStableCurrency(currency);\n return (\n <div className=\"flex items-center gap-2.5\">\n <PriceUnit\n chip={<FiatChip size={s.chip} />}\n amount={stripDollarSign(usdValue)}\n label={s.showLabel ? \"USD\" : undefined}\n amountClassName={amountClass}\n />\n <span className={`items-center gap-2.5 ${secondaryClassName ?? \"flex\"}`}>\n {!stable && <span className={`${s.divider} w-px bg-border/60 shrink-0`} />}\n {stable ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <CoinChip symbol={currency} size={s.stableChip} />\n <span className=\"text-sm font-semibold text-muted-foreground\">{currency}</span>\n </span>\n ) : (\n <PriceUnit\n chip={<CoinChip symbol={currency} size={s.chip} />}\n amount={cryptoDisplay}\n label={s.showLabel ? (currency ?? undefined) : undefined}\n amountClassName={amountClass}\n />\n )}\n </span>\n {trailing && <span className=\"ml-0.5\">{trailing}</span>}\n </div>\n );\n}\n\nconst CHIP_TONES = {\n\n \"overlay-dark\": { dot: \"text-white/30\", text: \"text-white/65\" },\n\n card: { dot: \"text-muted-foreground/50\", text: \"text-muted-foreground\" },\n\n /** Same solid color as the primary USD figure — for cards that want a\n * single consistent shade instead of a muted secondary currency. */\n solid: { dot: \"text-foreground/40\", text: \"text-foreground\" },\n} as const;\n\nexport function PriceChipContent({\n amountFormatted,\n currency,\n usdValue,\n tone,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n tone: keyof typeof CHIP_TONES;\n}) {\n if (!amountFormatted) return null;\n const cryptoDisplay = formatDisplayPrice(amountFormatted);\n const stable = isStableCurrency(currency);\n\n if (!usdValue) {\n return (\n <>\n {currency && <CurrencyIcon symbol={currency} size={13} />}\n {cryptoDisplay}\n </>\n );\n }\n\n const t = CHIP_TONES[tone];\n return (\n <>\n {stripDollarSign(usdValue)}\n <span className={`font-semibold ${t.text}`}>USD</span>\n <span className={t.dot}>·</span>\n <span className={`inline-flex items-center gap-1 font-semibold ${t.text}`}>\n {currency && <CurrencyIcon symbol={currency} size={12} />}\n {stable ? currency : cryptoDisplay}\n </span>\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -58,26 +58,34 @@ function StatTile({
58
58
  }
59
59
  );
60
60
  }
61
- function StatPill({ value, label, className }) {
62
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
63
- "div",
64
- {
65
- className: `inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1 text-sm ${className ?? ""}`,
66
- children: [
67
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-bold text-foreground tabular-nums", children: value }),
68
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-muted-foreground", children: label })
69
- ]
70
- }
71
- );
61
+ function StatPill({ value, label, className, active, onClick }) {
62
+ const classes = `inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-sm ${active ? "border-primary bg-primary/10 text-primary" : "border-border bg-card"} ${className ?? ""}`;
63
+ if (onClick) {
64
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", onClick, "aria-pressed": active, className: classes, children: [
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `font-bold tabular-nums ${active ? "" : "text-foreground"}`, children: value }),
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: active ? "" : "text-muted-foreground", children: label })
67
+ ] });
68
+ }
69
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: classes, children: [
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-bold text-foreground tabular-nums", children: value }),
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-muted-foreground", children: label })
72
+ ] });
72
73
  }
73
- function StatPillRow({ items, className }) {
74
- const shown = items.filter((i) => i.value !== void 0);
74
+ function StatPillRow({
75
+ items,
76
+ className,
77
+ onSelect,
78
+ activeIndex
79
+ }) {
80
+ const shown = items.map((item, index) => ({ item, index })).filter(({ item }) => item.value !== void 0);
75
81
  if (shown.length === 0) return null;
76
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `flex flex-wrap items-center gap-2 pt-0.5 ${className ?? ""}`, children: shown.map(({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
82
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `flex flex-wrap items-center gap-2 pt-0.5 ${className ?? ""}`, children: shown.map(({ item: { label, value }, index }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
77
83
  StatPill,
78
84
  {
79
85
  label,
80
- value: value == null ? "\u2014" : typeof value === "number" ? value.toLocaleString() : value
86
+ value: value == null ? "\u2014" : typeof value === "number" ? value.toLocaleString() : value,
87
+ active: onSelect ? activeIndex === index : void 0,
88
+ onClick: onSelect ? () => onSelect(index) : void 0
81
89
  },
82
90
  label
83
91
  )) });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/stat-tile.tsx"],"sourcesContent":["\n\nexport interface StatTileProps {\n label: string;\n\n value?: string | number | null;\n\n sub?: string;\n\n accent?: string;\n\n big?: boolean;\n radius?: number;\n children?: React.ReactNode;\n className?: string;\n}\n\nexport function StatTile({\n label,\n value,\n sub,\n accent,\n big,\n radius = 16,\n children,\n className,\n}: StatTileProps) {\n return (\n <div\n className={`bg-muted border border-border flex flex-col gap-1 min-w-0 ${className ?? ''}`}\n style={{ borderRadius: radius, padding: '12px 14px' }}\n >\n <span className=\"text-[10.5px] font-semibold tracking-[0.06em] uppercase text-muted-foreground\">\n {label}\n </span>\n {value != null && (\n <span\n className=\"font-semibold leading-none tracking-tight tabular-nums\"\n style={{\n fontSize: big ? 22 : 16,\n color: accent,\n }}\n >\n {value}\n </span>\n )}\n {sub && (\n <span className=\"text-2xs text-muted-foreground\">{sub}</span>\n )}\n {children}\n </div>\n );\n}\n\nexport interface StatPillProps {\n value: string | number;\n label: string;\n className?: string;\n}\n\nexport function StatPill({ value, label, className }: StatPillProps) {\n return (\n <div\n className={`inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1 text-sm ${className ?? ''}`}\n >\n <span className=\"font-bold text-foreground tabular-nums\">{value}</span>\n <span className=\"text-muted-foreground\">{label}</span>\n </div>\n );\n}\n\nexport interface StatPillRowItem {\n label: string;\n\n value?: number | string | null;\n}\n\nexport function StatPillRow({ items, className }: { items: StatPillRowItem[]; className?: string }) {\n const shown = items.filter((i) => i.value !== undefined);\n if (shown.length === 0) return null;\n\n return (\n <div className={`flex flex-wrap items-center gap-2 pt-0.5 ${className ?? ''}`}>\n {shown.map(({ label, value }) => (\n <StatPill\n key={label}\n label={label}\n value={value == null ? \"—\" : typeof value === \"number\" ? value.toLocaleString() : value}\n />\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BI;AAXG,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AACF,GAAkB;AAChB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,6DAA6D,aAAa,EAAE;AAAA,MACvF,OAAO,EAAE,cAAc,QAAQ,SAAS,YAAY;AAAA,MAEpD;AAAA,oDAAC,UAAK,WAAU,iFACb,iBACH;AAAA,QACC,SAAS,QACR;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,UAAU,MAAM,KAAK;AAAA,cACrB,OAAO;AAAA,YACT;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAED,OACC,4CAAC,UAAK,WAAU,kCAAkC,eAAI;AAAA,QAEvD;AAAA;AAAA;AAAA,EACH;AAEJ;AAQO,SAAS,SAAS,EAAE,OAAO,OAAO,UAAU,GAAkB;AACnE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,gGAAgG,aAAa,EAAE;AAAA,MAE1H;AAAA,oDAAC,UAAK,WAAU,0CAA0C,iBAAM;AAAA,QAChE,4CAAC,UAAK,WAAU,yBAAyB,iBAAM;AAAA;AAAA;AAAA,EACjD;AAEJ;AAQO,SAAS,YAAY,EAAE,OAAO,UAAU,GAAqD;AAClG,QAAM,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,UAAU,MAAS;AACvD,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,SACE,4CAAC,SAAI,WAAW,4CAA4C,aAAa,EAAE,IACxE,gBAAM,IAAI,CAAC,EAAE,OAAO,MAAM,MACzB;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA,OAAO,SAAS,OAAO,WAAM,OAAO,UAAU,WAAW,MAAM,eAAe,IAAI;AAAA;AAAA,IAF7E;AAAA,EAGP,CACD,GACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/stat-tile.tsx"],"sourcesContent":["\n\nexport interface StatTileProps {\n label: string;\n\n value?: string | number | null;\n\n sub?: string;\n\n accent?: string;\n\n big?: boolean;\n radius?: number;\n children?: React.ReactNode;\n className?: string;\n}\n\nexport function StatTile({\n label,\n value,\n sub,\n accent,\n big,\n radius = 16,\n children,\n className,\n}: StatTileProps) {\n return (\n <div\n className={`bg-muted border border-border flex flex-col gap-1 min-w-0 ${className ?? ''}`}\n style={{ borderRadius: radius, padding: '12px 14px' }}\n >\n <span className=\"text-[10.5px] font-semibold tracking-[0.06em] uppercase text-muted-foreground\">\n {label}\n </span>\n {value != null && (\n <span\n className=\"font-semibold leading-none tracking-tight tabular-nums\"\n style={{\n fontSize: big ? 22 : 16,\n color: accent,\n }}\n >\n {value}\n </span>\n )}\n {sub && (\n <span className=\"text-2xs text-muted-foreground\">{sub}</span>\n )}\n {children}\n </div>\n );\n}\n\nexport interface StatPillProps {\n value: string | number;\n label: string;\n className?: string;\n active?: boolean;\n onClick?: () => void;\n}\n\nexport function StatPill({ value, label, className, active, onClick }: StatPillProps) {\n const classes = `inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-sm ${\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border bg-card\"\n } ${className ?? ''}`;\n\n if (onClick) {\n return (\n <button type=\"button\" onClick={onClick} aria-pressed={active} className={classes}>\n <span className={`font-bold tabular-nums ${active ? \"\" : \"text-foreground\"}`}>{value}</span>\n <span className={active ? \"\" : \"text-muted-foreground\"}>{label}</span>\n </button>\n );\n }\n\n return (\n <div className={classes}>\n <span className=\"font-bold text-foreground tabular-nums\">{value}</span>\n <span className=\"text-muted-foreground\">{label}</span>\n </div>\n );\n}\n\nexport interface StatPillRowItem {\n label: string;\n\n value?: number | string | null;\n}\n\nexport function StatPillRow({\n items,\n className,\n onSelect,\n activeIndex,\n}: {\n items: StatPillRowItem[];\n className?: string;\n onSelect?: (index: number) => void;\n activeIndex?: number;\n}) {\n const shown = items\n .map((item, index) => ({ item, index }))\n .filter(({ item }) => item.value !== undefined);\n if (shown.length === 0) return null;\n\n return (\n <div className={`flex flex-wrap items-center gap-2 pt-0.5 ${className ?? ''}`}>\n {shown.map(({ item: { label, value }, index }) => (\n <StatPill\n key={label}\n label={label}\n value={value == null ? \"—\" : typeof value === \"number\" ? value.toLocaleString() : value}\n active={onSelect ? activeIndex === index : undefined}\n onClick={onSelect ? () => onSelect(index) : undefined}\n />\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BI;AAXG,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AACF,GAAkB;AAChB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,6DAA6D,aAAa,EAAE;AAAA,MACvF,OAAO,EAAE,cAAc,QAAQ,SAAS,YAAY;AAAA,MAEpD;AAAA,oDAAC,UAAK,WAAU,iFACb,iBACH;AAAA,QACC,SAAS,QACR;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,UAAU,MAAM,KAAK;AAAA,cACrB,OAAO;AAAA,YACT;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAED,OACC,4CAAC,UAAK,WAAU,kCAAkC,eAAI;AAAA,QAEvD;AAAA;AAAA;AAAA,EACH;AAEJ;AAUO,SAAS,SAAS,EAAE,OAAO,OAAO,WAAW,QAAQ,QAAQ,GAAkB;AACpF,QAAM,UAAU,0EACd,SAAS,8CAA8C,uBACzD,IAAI,aAAa,EAAE;AAEnB,MAAI,SAAS;AACX,WACE,6CAAC,YAAO,MAAK,UAAS,SAAkB,gBAAc,QAAQ,WAAW,SACvE;AAAA,kDAAC,UAAK,WAAW,0BAA0B,SAAS,KAAK,iBAAiB,IAAK,iBAAM;AAAA,MACrF,4CAAC,UAAK,WAAW,SAAS,KAAK,yBAA0B,iBAAM;AAAA,OACjE;AAAA,EAEJ;AAEA,SACE,6CAAC,SAAI,WAAW,SACd;AAAA,gDAAC,UAAK,WAAU,0CAA0C,iBAAM;AAAA,IAChE,4CAAC,UAAK,WAAU,yBAAyB,iBAAM;AAAA,KACjD;AAEJ;AAQO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,QAAQ,MACX,IAAI,CAAC,MAAM,WAAW,EAAE,MAAM,MAAM,EAAE,EACtC,OAAO,CAAC,EAAE,KAAK,MAAM,KAAK,UAAU,MAAS;AAChD,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,SACE,4CAAC,SAAI,WAAW,4CAA4C,aAAa,EAAE,IACxE,gBAAM,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,MAAM,GAAG,MAAM,MAC1C;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA,OAAO,SAAS,OAAO,WAAM,OAAO,UAAU,WAAW,MAAM,eAAe,IAAI;AAAA,MAClF,QAAQ,WAAW,gBAAgB,QAAQ;AAAA,MAC3C,SAAS,WAAW,MAAM,SAAS,KAAK,IAAI;AAAA;AAAA,IAJvC;AAAA,EAKP,CACD,GACH;AAEJ;","names":[]}
@@ -15,15 +15,19 @@ interface StatPillProps {
15
15
  value: string | number;
16
16
  label: string;
17
17
  className?: string;
18
+ active?: boolean;
19
+ onClick?: () => void;
18
20
  }
19
- declare function StatPill({ value, label, className }: StatPillProps): react_jsx_runtime.JSX.Element;
21
+ declare function StatPill({ value, label, className, active, onClick }: StatPillProps): react_jsx_runtime.JSX.Element;
20
22
  interface StatPillRowItem {
21
23
  label: string;
22
24
  value?: number | string | null;
23
25
  }
24
- declare function StatPillRow({ items, className }: {
26
+ declare function StatPillRow({ items, className, onSelect, activeIndex, }: {
25
27
  items: StatPillRowItem[];
26
28
  className?: string;
29
+ onSelect?: (index: number) => void;
30
+ activeIndex?: number;
27
31
  }): react_jsx_runtime.JSX.Element | null;
28
32
 
29
33
  export { StatPill, type StatPillProps, StatPillRow, type StatPillRowItem, StatTile, type StatTileProps };
@@ -15,15 +15,19 @@ interface StatPillProps {
15
15
  value: string | number;
16
16
  label: string;
17
17
  className?: string;
18
+ active?: boolean;
19
+ onClick?: () => void;
18
20
  }
19
- declare function StatPill({ value, label, className }: StatPillProps): react_jsx_runtime.JSX.Element;
21
+ declare function StatPill({ value, label, className, active, onClick }: StatPillProps): react_jsx_runtime.JSX.Element;
20
22
  interface StatPillRowItem {
21
23
  label: string;
22
24
  value?: number | string | null;
23
25
  }
24
- declare function StatPillRow({ items, className }: {
26
+ declare function StatPillRow({ items, className, onSelect, activeIndex, }: {
25
27
  items: StatPillRowItem[];
26
28
  className?: string;
29
+ onSelect?: (index: number) => void;
30
+ activeIndex?: number;
27
31
  }): react_jsx_runtime.JSX.Element | null;
28
32
 
29
33
  export { StatPill, type StatPillProps, StatPillRow, type StatPillRowItem, StatTile, type StatTileProps };