@medialane/ui 0.285.0 → 0.286.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 (35) hide show
  1. package/dist/components/account/input-otp.d.cts +2 -2
  2. package/dist/components/account/input-otp.d.ts +2 -2
  3. package/dist/components/button.d.cts +2 -2
  4. package/dist/components/button.d.ts +2 -2
  5. package/dist/components/fast-mint/fast-mint.d.cts +2 -2
  6. package/dist/components/fast-mint/fast-mint.d.ts +2 -2
  7. package/dist/components/launchpad/edition-mint-schema.d.cts +2 -2
  8. package/dist/components/launchpad/edition-mint-schema.d.ts +2 -2
  9. package/dist/components/marketplace-filters.cjs +253 -0
  10. package/dist/components/marketplace-filters.cjs.map +1 -0
  11. package/dist/components/marketplace-filters.d.cts +34 -0
  12. package/dist/components/marketplace-filters.d.ts +34 -0
  13. package/dist/components/marketplace-filters.js +216 -0
  14. package/dist/components/marketplace-filters.js.map +1 -0
  15. package/dist/components/marketplace-search.cjs +175 -0
  16. package/dist/components/marketplace-search.cjs.map +1 -0
  17. package/dist/components/marketplace-search.d.cts +11 -0
  18. package/dist/components/marketplace-search.d.ts +11 -0
  19. package/dist/components/marketplace-search.js +140 -0
  20. package/dist/components/marketplace-search.js.map +1 -0
  21. package/dist/components/sheet.d.cts +1 -1
  22. package/dist/components/sheet.d.ts +1 -1
  23. package/dist/index.cjs +17 -0
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +3 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.js +10 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/utils/use-debounced-value.cjs +38 -0
  30. package/dist/utils/use-debounced-value.cjs.map +1 -0
  31. package/dist/utils/use-debounced-value.d.cts +3 -0
  32. package/dist/utils/use-debounced-value.d.ts +3 -0
  33. package/dist/utils/use-debounced-value.js +14 -0
  34. package/dist/utils/use-debounced-value.js.map +1 -0
  35. package/package.json +1 -1
@@ -0,0 +1,216 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import Link from "./link.js";
5
+ import { usePathname } from "next/navigation";
6
+ import { SlidersHorizontal } from "lucide-react";
7
+ import { getTokenBySymbol, parseAmount, SUPPORTED_TOKENS } from "@medialane/sdk";
8
+ import { Input } from "./input.js";
9
+ import { Button } from "./button.js";
10
+ import { Dialog, DialogContent, DialogTitle } from "./dialog.js";
11
+ import { CurrencyIcon } from "./currency-icon.js";
12
+ import { HelpIcon } from "./help-icon.js";
13
+ import { IP_TYPES } from "../data/ip.js";
14
+ import { useDebouncedValue } from "../utils/use-debounced-value.js";
15
+ import { cn } from "../utils/cn.js";
16
+ const SORT_OPTIONS = [
17
+ { label: "Recent", value: "recent" },
18
+ { label: "Price \u2191", value: "price_asc" },
19
+ { label: "Price \u2193", value: "price_desc" }
20
+ ];
21
+ const TYPE_OPTIONS = [
22
+ { label: "All", value: "" },
23
+ { label: "Listings", value: "listings" },
24
+ { label: "Offers", value: "offers" }
25
+ ];
26
+ const CURRENCY_OPTIONS = SUPPORTED_TOKENS.map((t) => ({
27
+ label: t.symbol,
28
+ value: t.symbol,
29
+ icon: /* @__PURE__ */ jsx(CurrencyIcon, { symbol: t.symbol, size: 13 })
30
+ }));
31
+ function toRawAmount(value, decimals) {
32
+ const trimmed = value.trim();
33
+ if (!trimmed) return void 0;
34
+ try {
35
+ return parseAmount(trimmed, decimals);
36
+ } catch {
37
+ return void 0;
38
+ }
39
+ }
40
+ function useMarketplaceFilters() {
41
+ const [open, setOpen] = useState(false);
42
+ const [sort, setSort] = useState("recent");
43
+ const [currency, setCurrency] = useState("");
44
+ const [orderType, setOrderType] = useState("");
45
+ const [minInput, setMinInput] = useState("");
46
+ const [maxInput, setMaxInput] = useState("");
47
+ const debouncedMin = useDebouncedValue(minInput, 400);
48
+ const debouncedMax = useDebouncedValue(maxInput, 400);
49
+ const decimals = getTokenBySymbol(currency)?.decimals ?? 18;
50
+ const minPrice = minInput ? toRawAmount(debouncedMin, decimals) : void 0;
51
+ const maxPrice = maxInput ? toRawAmount(debouncedMax, decimals) : void 0;
52
+ const clearPrice = () => {
53
+ setMinInput("");
54
+ setMaxInput("");
55
+ };
56
+ const selectCurrency = (symbol) => {
57
+ setCurrency(currency === symbol ? "" : symbol);
58
+ clearPrice();
59
+ };
60
+ const reset = () => {
61
+ setSort("recent");
62
+ setCurrency("");
63
+ setOrderType("");
64
+ clearPrice();
65
+ };
66
+ const count = [sort !== "recent", currency, orderType, minPrice || maxPrice].filter(Boolean).length;
67
+ return {
68
+ open,
69
+ setOpen,
70
+ sort,
71
+ setSort,
72
+ currency,
73
+ selectCurrency,
74
+ orderType,
75
+ setOrderType,
76
+ minInput,
77
+ setMinInput,
78
+ maxInput,
79
+ setMaxInput,
80
+ minPrice,
81
+ maxPrice,
82
+ clearPrice,
83
+ reset,
84
+ count
85
+ };
86
+ }
87
+ const chipClass = (active) => cn(
88
+ "inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap",
89
+ active ? "border-primary bg-primary/10 text-primary font-medium" : "border-border text-muted-foreground hover:border-primary/50"
90
+ );
91
+ function ChipGroup({ title, options, value, onSelect }) {
92
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
93
+ /* @__PURE__ */ jsx("p", { className: "text-2xs font-semibold text-muted-foreground", children: title }),
94
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: options.map((opt) => /* @__PURE__ */ jsxs("button", { onClick: () => onSelect(opt.value), className: chipClass(value === opt.value), children: [
95
+ opt.icon,
96
+ /* @__PURE__ */ jsx("span", { children: opt.label })
97
+ ] }, opt.value)) })
98
+ ] });
99
+ }
100
+ function IpTypeChip({ href, label }) {
101
+ const pathname = usePathname();
102
+ return /* @__PURE__ */ jsx(
103
+ Link,
104
+ {
105
+ href,
106
+ className: cn(
107
+ "text-xs px-3 py-1 rounded-full border transition-colors whitespace-nowrap",
108
+ pathname === href ? "border-primary bg-primary/10 text-primary font-medium" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
109
+ ),
110
+ children: label
111
+ }
112
+ );
113
+ }
114
+ function ActiveChip({ children, onClear }) {
115
+ return /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary", children: [
116
+ children,
117
+ /* @__PURE__ */ jsx("button", { onClick: onClear, className: "ml-0.5 hover:text-primary/60", children: "\xD7" })
118
+ ] });
119
+ }
120
+ function FiltersButton({ filters }) {
121
+ return /* @__PURE__ */ jsxs(
122
+ "button",
123
+ {
124
+ onClick: () => filters.setOpen(true),
125
+ className: cn(
126
+ "relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors shrink-0",
127
+ filters.count > 0 ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:border-primary/50 hover:text-foreground"
128
+ ),
129
+ children: [
130
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
131
+ "Filters",
132
+ filters.count > 0 && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground", children: filters.count })
133
+ ]
134
+ }
135
+ );
136
+ }
137
+ function ActiveFilters({ filters }) {
138
+ const { sort, setSort, orderType, setOrderType, currency, selectCurrency, minPrice, maxPrice, minInput, maxInput, clearPrice, count } = filters;
139
+ if (count === 0) return null;
140
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
141
+ sort !== "recent" && /* @__PURE__ */ jsx(ActiveChip, { onClear: () => setSort("recent"), children: SORT_OPTIONS.find((o) => o.value === sort)?.label }),
142
+ orderType && /* @__PURE__ */ jsx(ActiveChip, { onClear: () => setOrderType(""), children: TYPE_OPTIONS.find((o) => o.value === orderType)?.label }),
143
+ currency && /* @__PURE__ */ jsxs(ActiveChip, { onClear: () => selectCurrency(currency), children: [
144
+ /* @__PURE__ */ jsx(CurrencyIcon, { symbol: currency, size: 12 }),
145
+ currency
146
+ ] }),
147
+ (minPrice || maxPrice) && /* @__PURE__ */ jsxs(ActiveChip, { onClear: clearPrice, children: [
148
+ minInput || "0",
149
+ " \u2013 ",
150
+ maxInput || "\u221E"
151
+ ] })
152
+ ] });
153
+ }
154
+ function FiltersDialog({ filters }) {
155
+ const { open, setOpen, sort, setSort, orderType, setOrderType, currency, selectCurrency, minInput, setMinInput, maxInput, setMaxInput, reset, count } = filters;
156
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]", children: [
157
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12", children: [
158
+ /* @__PURE__ */ jsxs(DialogTitle, { className: "text-base font-bold flex items-center gap-2", children: [
159
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-4 w-4 text-primary" }),
160
+ "Filters"
161
+ ] }),
162
+ count > 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", className: "h-7 text-xs text-muted-foreground", onClick: reset, children: "Clear all" })
163
+ ] }),
164
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-4 space-y-5", children: [
165
+ /* @__PURE__ */ jsx(ChipGroup, { title: "Sort", options: SORT_OPTIONS, value: sort, onSelect: setSort }),
166
+ /* @__PURE__ */ jsx(ChipGroup, { title: "Order type", options: TYPE_OPTIONS, value: orderType, onSelect: setOrderType }),
167
+ /* @__PURE__ */ jsx(ChipGroup, { title: "Currency", options: CURRENCY_OPTIONS, value: currency, onSelect: selectCurrency }),
168
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
169
+ /* @__PURE__ */ jsx("p", { className: "text-2xs font-semibold text-muted-foreground", children: "Price range" }),
170
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
171
+ /* @__PURE__ */ jsx(
172
+ Input,
173
+ {
174
+ placeholder: "Min",
175
+ value: minInput,
176
+ onChange: (e) => setMinInput(e.target.value),
177
+ className: "h-9 text-sm",
178
+ type: "number",
179
+ min: "0"
180
+ }
181
+ ),
182
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground shrink-0", children: "\u2013" }),
183
+ /* @__PURE__ */ jsx(
184
+ Input,
185
+ {
186
+ placeholder: "Max",
187
+ value: maxInput,
188
+ onChange: (e) => setMaxInput(e.target.value),
189
+ className: "h-9 text-sm",
190
+ type: "number",
191
+ min: "0"
192
+ }
193
+ )
194
+ ] })
195
+ ] }),
196
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 pt-1 border-t border-border/60", children: [
197
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1 text-2xs font-semibold text-muted-foreground", children: [
198
+ "IP Type",
199
+ /* @__PURE__ */ jsx(HelpIcon, { content: "Filter by intellectual property category \u2014 Art, Audio, Video, Software, and more", side: "right" })
200
+ ] }),
201
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
202
+ /* @__PURE__ */ jsx(IpTypeChip, { href: "/marketplace", label: "All" }),
203
+ IP_TYPES.map((type) => /* @__PURE__ */ jsx(IpTypeChip, { href: `/${type.toLowerCase()}`, label: type }, type))
204
+ ] })
205
+ ] })
206
+ ] }),
207
+ /* @__PURE__ */ jsx("div", { className: "px-5 py-3 border-t border-border/60", children: /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: () => setOpen(false), children: "Apply filters" }) })
208
+ ] }) });
209
+ }
210
+ export {
211
+ ActiveFilters,
212
+ FiltersButton,
213
+ FiltersDialog,
214
+ useMarketplaceFilters
215
+ };
216
+ //# sourceMappingURL=marketplace-filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/marketplace-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport Link from \"./link.js\";\nimport { usePathname } from \"next/navigation\";\nimport { SlidersHorizontal } from \"lucide-react\";\nimport { getTokenBySymbol, parseAmount, SUPPORTED_TOKENS } from \"@medialane/sdk\";\nimport { Input } from \"./input.js\";\nimport { Button } from \"./button.js\";\nimport { Dialog, DialogContent, DialogTitle } from \"./dialog.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { HelpIcon } from \"./help-icon.js\";\nimport { IP_TYPES } from \"../data/ip.js\";\nimport { useDebouncedValue } from \"../utils/use-debounced-value.js\";\nimport { cn } from \"../utils/cn.js\";\n\nconst SORT_OPTIONS = [\n { label: \"Recent\", value: \"recent\" },\n { label: \"Price ↑\", value: \"price_asc\" },\n { label: \"Price ↓\", value: \"price_desc\" },\n];\n\nconst TYPE_OPTIONS = [\n { label: \"All\", value: \"\" },\n { label: \"Listings\", value: \"listings\" },\n { label: \"Offers\", value: \"offers\" },\n];\n\nconst CURRENCY_OPTIONS = SUPPORTED_TOKENS.map((t) => ({\n label: t.symbol,\n value: t.symbol,\n icon: <CurrencyIcon symbol={t.symbol} size={13} />,\n}));\n\nfunction toRawAmount(value: string, decimals: number): string | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n try {\n return parseAmount(trimmed, decimals);\n } catch {\n return undefined;\n }\n}\n\nexport function useMarketplaceFilters() {\n const [open, setOpen] = useState(false);\n const [sort, setSort] = useState(\"recent\");\n const [currency, setCurrency] = useState(\"\");\n const [orderType, setOrderType] = useState(\"\");\n const [minInput, setMinInput] = useState(\"\");\n const [maxInput, setMaxInput] = useState(\"\");\n const debouncedMin = useDebouncedValue(minInput, 400);\n const debouncedMax = useDebouncedValue(maxInput, 400);\n\n const decimals = getTokenBySymbol(currency)?.decimals ?? 18;\n const minPrice = minInput ? toRawAmount(debouncedMin, decimals) : undefined;\n const maxPrice = maxInput ? toRawAmount(debouncedMax, decimals) : undefined;\n\n const clearPrice = () => {\n setMinInput(\"\");\n setMaxInput(\"\");\n };\n\n const selectCurrency = (symbol: string) => {\n setCurrency(currency === symbol ? \"\" : symbol);\n clearPrice();\n };\n\n const reset = () => {\n setSort(\"recent\");\n setCurrency(\"\");\n setOrderType(\"\");\n clearPrice();\n };\n\n const count = [sort !== \"recent\", currency, orderType, minPrice || maxPrice].filter(Boolean).length;\n\n return {\n open, setOpen,\n sort, setSort,\n currency, selectCurrency,\n orderType, setOrderType,\n minInput, setMinInput,\n maxInput, setMaxInput,\n minPrice, maxPrice,\n clearPrice, reset, count,\n };\n}\n\nexport type MarketplaceFilters = ReturnType<typeof useMarketplaceFilters>;\n\nconst chipClass = (active: boolean) =>\n cn(\n \"inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full border transition-colors whitespace-nowrap\",\n active\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50\"\n );\n\nfunction ChipGroup({ title, options, value, onSelect }: {\n title: string;\n options: { label: string; value: string; icon?: ReactNode }[];\n value: string;\n onSelect: (value: string) => void;\n}) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">{title}</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {options.map((opt) => (\n <button key={opt.value} onClick={() => onSelect(opt.value)} className={chipClass(value === opt.value)}>\n {opt.icon}\n <span>{opt.label}</span>\n </button>\n ))}\n </div>\n </div>\n );\n}\n\nfunction IpTypeChip({ href, label }: { href: string; label: string }) {\n const pathname = usePathname();\n return (\n <Link\n href={href}\n className={cn(\n \"text-xs px-3 py-1 rounded-full border transition-colors whitespace-nowrap\",\n pathname === href\n ? \"border-primary bg-primary/10 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {label}\n </Link>\n );\n}\n\nfunction ActiveChip({ children, onClear }: { children: ReactNode; onClear: () => void }) {\n return (\n <span className=\"flex items-center gap-1 text-xs px-2.5 py-1 rounded-full border border-primary/40 bg-primary/10 text-primary\">\n {children}\n <button onClick={onClear} className=\"ml-0.5 hover:text-primary/60\">×</button>\n </span>\n );\n}\n\nexport function FiltersButton({ filters }: { filters: MarketplaceFilters }) {\n return (\n <button\n onClick={() => filters.setOpen(true)}\n className={cn(\n \"relative flex items-center gap-1.5 h-9 px-3 rounded-lg border text-xs font-medium transition-colors shrink-0\",\n filters.count > 0\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filters.count > 0 && (\n <span className=\"flex h-4 w-4 items-center justify-center rounded-full bg-primary text-2xs font-bold text-primary-foreground\">\n {filters.count}\n </span>\n )}\n </button>\n );\n}\n\nexport function ActiveFilters({ filters }: { filters: MarketplaceFilters }) {\n const { sort, setSort, orderType, setOrderType, currency, selectCurrency, minPrice, maxPrice, minInput, maxInput, clearPrice, count } = filters;\n if (count === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {sort !== \"recent\" && (\n <ActiveChip onClear={() => setSort(\"recent\")}>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</ActiveChip>\n )}\n {orderType && (\n <ActiveChip onClear={() => setOrderType(\"\")}>{TYPE_OPTIONS.find((o) => o.value === orderType)?.label}</ActiveChip>\n )}\n {currency && (\n <ActiveChip onClear={() => selectCurrency(currency)}>\n <CurrencyIcon symbol={currency} size={12} />\n {currency}\n </ActiveChip>\n )}\n {(minPrice || maxPrice) && (\n <ActiveChip onClear={clearPrice}>{minInput || \"0\"} – {maxInput || \"∞\"}</ActiveChip>\n )}\n </div>\n );\n}\n\nexport function FiltersDialog({ filters }: { filters: MarketplaceFilters }) {\n const { open, setOpen, sort, setSort, orderType, setOrderType, currency, selectCurrency, minInput, setMinInput, maxInput, setMaxInput, reset, count } = filters;\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"w-full max-w-sm sm:max-w-md p-0 overflow-hidden gap-0 flex flex-col max-h-[85svh]\">\n\n <div className=\"flex items-center justify-between px-5 py-4 border-b border-border/60 pr-12\">\n <DialogTitle className=\"text-base font-bold flex items-center gap-2\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </DialogTitle>\n {count > 0 && (\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 text-xs text-muted-foreground\" onClick={reset}>\n Clear all\n </Button>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4 space-y-5\">\n <ChipGroup title=\"Sort\" options={SORT_OPTIONS} value={sort} onSelect={setSort} />\n <ChipGroup title=\"Order type\" options={TYPE_OPTIONS} value={orderType} onSelect={setOrderType} />\n <ChipGroup title=\"Currency\" options={CURRENCY_OPTIONS} value={currency} onSelect={selectCurrency} />\n\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-semibold text-muted-foreground\">Price range</p>\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder=\"Min\"\n value={minInput}\n onChange={(e) => setMinInput(e.target.value)}\n className=\"h-9 text-sm\"\n type=\"number\"\n min=\"0\"\n />\n <span className=\"text-sm text-muted-foreground shrink-0\">–</span>\n <Input\n placeholder=\"Max\"\n value={maxInput}\n onChange={(e) => setMaxInput(e.target.value)}\n className=\"h-9 text-sm\"\n type=\"number\"\n min=\"0\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2 pt-1 border-t border-border/60\">\n <p className=\"flex items-center gap-1 text-2xs font-semibold text-muted-foreground\">\n IP Type\n <HelpIcon content=\"Filter by intellectual property category — Art, Audio, Video, Software, and more\" side=\"right\" />\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n <IpTypeChip href=\"/marketplace\" label=\"All\" />\n {IP_TYPES.map((type) => (\n <IpTypeChip key={type} href={`/${type.toLowerCase()}`} label={type} />\n ))}\n </div>\n </div>\n </div>\n\n <div className=\"px-5 py-3 border-t border-border/60\">\n <Button className=\"w-full\" onClick={() => setOpen(false)}>\n Apply filters\n </Button>\n </div>\n\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var marketplace_search_exports = {};
31
+ __export(marketplace_search_exports, {
32
+ MarketplaceSearch: () => MarketplaceSearch,
33
+ SearchInput: () => SearchInput
34
+ });
35
+ module.exports = __toCommonJS(marketplace_search_exports);
36
+ var import_jsx_runtime = require("react/jsx-runtime");
37
+ var import_react = require("react");
38
+ var import_navigation = require("next/navigation");
39
+ var import_image = __toESM(require("./image.js"), 1);
40
+ var import_link = __toESM(require("./link.js"), 1);
41
+ var import_lucide_react = require("lucide-react");
42
+ var import_sdk = require("@medialane/sdk");
43
+ var import_input = require("./input.js");
44
+ var import_use_medialane_client = require("../utils/use-medialane-client.js");
45
+ var import_use_debounced_value = require("../utils/use-debounced-value.js");
46
+ var import_use_image_url = require("../utils/use-image-url.js");
47
+ function SearchInput({ value, onClear, ...props }) {
48
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative", children: [
49
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
50
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_input.Input, { value, className: "pl-9 pr-9", ...props }),
51
+ value && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
52
+ "button",
53
+ {
54
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
55
+ onClick: onClear,
56
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "h-3.5 w-3.5" })
57
+ }
58
+ )
59
+ ] });
60
+ }
61
+ function MarketplaceSearch() {
62
+ const resolveImage = (0, import_use_image_url.useImageUrl)();
63
+ const client = (0, import_use_medialane_client.useMedialaneClient)();
64
+ const router = (0, import_navigation.useRouter)();
65
+ const [query, setQuery] = (0, import_react.useState)("");
66
+ const [results, setResults] = (0, import_react.useState)(null);
67
+ const [open, setOpen] = (0, import_react.useState)(false);
68
+ const debouncedQuery = (0, import_use_debounced_value.useDebouncedValue)(query.trim(), 300);
69
+ (0, import_react.useEffect)(() => {
70
+ if (!debouncedQuery) {
71
+ setResults(null);
72
+ setOpen(false);
73
+ return;
74
+ }
75
+ let current = true;
76
+ client.api.search(debouncedQuery, 8).then((res) => {
77
+ if (!current) return;
78
+ setResults(res.data);
79
+ setOpen(true);
80
+ }).catch(() => {
81
+ if (current) setResults(null);
82
+ });
83
+ return () => {
84
+ current = false;
85
+ };
86
+ }, [debouncedQuery, client]);
87
+ const handleKeyDown = (e) => {
88
+ if (e.key === "Enter" && query.trim()) {
89
+ setOpen(false);
90
+ router.push(`/search?q=${encodeURIComponent(query.trim())}`);
91
+ }
92
+ };
93
+ const hasResults = results && ((results.tokens?.length ?? 0) > 0 || (results.collections?.length ?? 0) > 0);
94
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative w-full sm:max-w-sm", children: [
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
96
+ SearchInput,
97
+ {
98
+ placeholder: "Search creative works",
99
+ value: query,
100
+ onChange: (e) => setQuery(e.target.value),
101
+ onKeyDown: handleKeyDown,
102
+ onFocus: () => hasResults && setOpen(true),
103
+ onBlur: () => setOpen(false),
104
+ onClear: () => {
105
+ setQuery("");
106
+ setResults(null);
107
+ setOpen(false);
108
+ }
109
+ }
110
+ ),
111
+ open && hasResults && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
112
+ "div",
113
+ {
114
+ onMouseDown: (e) => e.preventDefault(),
115
+ className: "absolute top-full left-0 right-0 mt-1 bg-popover border border-border rounded-xl shadow-lg z-50 overflow-hidden",
116
+ children: [
117
+ (results.tokens?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30", children: "Tokens" }),
119
+ results.tokens.map((t) => {
120
+ const img = resolveImage(t.image);
121
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
122
+ import_link.default,
123
+ {
124
+ href: (0, import_sdk.assetHref)("STARKNET", t.contractAddress, t.tokenId),
125
+ className: "flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors",
126
+ children: [
127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative h-8 w-8 rounded-lg bg-muted shrink-0 overflow-hidden", children: img && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_image.default, { src: img, alt: "", fill: true, className: "object-cover", unoptimized: true }) }),
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-medium truncate", children: t.name ?? `Token #${t.tokenId}` }),
130
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground tabular-nums truncate", children: [
131
+ t.contractAddress.slice(0, 14),
132
+ "\u2026"
133
+ ] })
134
+ ] })
135
+ ]
136
+ },
137
+ `${t.contractAddress}-${t.tokenId}`
138
+ );
139
+ })
140
+ ] }),
141
+ (results.collections?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30", children: "Collections" }),
143
+ results.collections.map((c) => {
144
+ const imgUrl = resolveImage(c.image);
145
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
146
+ import_link.default,
147
+ {
148
+ href: (0, import_sdk.collectionHref)("STARKNET", c.contractAddress),
149
+ className: "flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors",
150
+ children: [
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative h-8 w-8 rounded-lg bg-gradient-to-br from-primary/20 to-brand-purple/20 flex items-center justify-center text-xs font-bold shrink-0 overflow-hidden", children: imgUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_image.default, { src: imgUrl, alt: "", fill: true, className: "object-cover", unoptimized: true }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: c.name?.charAt(0) ?? "?" }) }),
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-medium truncate", children: c.name }),
154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground tabular-nums truncate", children: [
155
+ c.contractAddress.slice(0, 14),
156
+ "\u2026"
157
+ ] })
158
+ ] })
159
+ ]
160
+ },
161
+ c.contractAddress
162
+ );
163
+ })
164
+ ] })
165
+ ]
166
+ }
167
+ )
168
+ ] });
169
+ }
170
+ // Annotate the CommonJS export names for ESM import in node:
171
+ 0 && (module.exports = {
172
+ MarketplaceSearch,
173
+ SearchInput
174
+ });
175
+ //# sourceMappingURL=marketplace-search.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/marketplace-search.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState, type ComponentProps } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport Image from \"./image.js\";\nimport Link from \"./link.js\";\nimport { Search, X } from \"lucide-react\";\nimport { assetHref, collectionHref, type ApiSearchResult } from \"@medialane/sdk\";\nimport { Input } from \"./input.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useDebouncedValue } from \"../utils/use-debounced-value.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nexport function SearchInput({ value, onClear, ...props }: ComponentProps<typeof Input> & { value: string; onClear: () => void }) {\n return (\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input value={value} className=\"pl-9 pr-9\" {...props} />\n {value && (\n <button\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n onClick={onClear}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n );\n}\n\nexport function MarketplaceSearch() {\n const resolveImage = useImageUrl();\n const client = useMedialaneClient();\n const router = useRouter();\n const [query, setQuery] = useState(\"\");\n const [results, setResults] = useState<ApiSearchResult | null>(null);\n const [open, setOpen] = useState(false);\n const debouncedQuery = useDebouncedValue(query.trim(), 300);\n\n useEffect(() => {\n if (!debouncedQuery) {\n setResults(null);\n setOpen(false);\n return;\n }\n let current = true;\n client.api\n .search(debouncedQuery, 8)\n .then((res) => {\n if (!current) return;\n setResults(res.data);\n setOpen(true);\n })\n .catch(() => {\n if (current) setResults(null);\n });\n return () => {\n current = false;\n };\n }, [debouncedQuery, client]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && query.trim()) {\n setOpen(false);\n router.push(`/search?q=${encodeURIComponent(query.trim())}`);\n }\n };\n\n const hasResults =\n results && ((results.tokens?.length ?? 0) > 0 || (results.collections?.length ?? 0) > 0);\n\n return (\n <div className=\"relative w-full sm:max-w-sm\">\n <SearchInput\n placeholder=\"Search creative works\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n onFocus={() => hasResults && setOpen(true)}\n onBlur={() => setOpen(false)}\n onClear={() => { setQuery(\"\"); setResults(null); setOpen(false); }}\n />\n\n {open && hasResults && (\n <div\n onMouseDown={(e) => e.preventDefault()}\n className=\"absolute top-full left-0 right-0 mt-1 bg-popover border border-border rounded-xl shadow-lg z-50 overflow-hidden\"\n >\n {(results.tokens?.length ?? 0) > 0 && (\n <div>\n <div className=\"px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30\">\n Tokens\n </div>\n {results.tokens!.map((t) => {\n const img = resolveImage(t.image);\n return (\n <Link\n key={`${t.contractAddress}-${t.tokenId}`}\n href={assetHref(\"STARKNET\", t.contractAddress, t.tokenId)}\n className=\"flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"relative h-8 w-8 rounded-lg bg-muted shrink-0 overflow-hidden\">\n {img && <Image src={img} alt=\"\" fill className=\"object-cover\" unoptimized />}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">{t.name ?? `Token #${t.tokenId}`}</p>\n <p className=\"text-xs text-muted-foreground tabular-nums truncate\">{t.contractAddress.slice(0, 14)}…</p>\n </div>\n </Link>\n );\n })}\n </div>\n )}\n {(results.collections?.length ?? 0) > 0 && (\n <div>\n <div className=\"px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30\">\n Collections\n </div>\n {results.collections!.map((c) => {\n const imgUrl = resolveImage(c.image);\n return (\n <Link\n key={c.contractAddress}\n href={collectionHref(\"STARKNET\", c.contractAddress)}\n className=\"flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"relative h-8 w-8 rounded-lg bg-gradient-to-br from-primary/20 to-brand-purple/20 flex items-center justify-center text-xs font-bold shrink-0 overflow-hidden\">\n {imgUrl ? (\n <Image src={imgUrl} alt=\"\" fill className=\"object-cover\" unoptimized />\n ) : (\n <span>{c.name?.charAt(0) ?? \"?\"}</span>\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">{c.name}</p>\n <p className=\"text-xs text-muted-foreground tabular-nums truncate\">{c.contractAddress.slice(0, 14)}…</p>\n </div>\n </Link>\n );\n })}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["Link","Image"]}
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ComponentProps } from 'react';
3
+ import { Input } from './input.cjs';
4
+
5
+ declare function SearchInput({ value, onClear, ...props }: ComponentProps<typeof Input> & {
6
+ value: string;
7
+ onClear: () => void;
8
+ }): react_jsx_runtime.JSX.Element;
9
+ declare function MarketplaceSearch(): react_jsx_runtime.JSX.Element;
10
+
11
+ export { MarketplaceSearch, SearchInput };
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ComponentProps } from 'react';
3
+ import { Input } from './input.js';
4
+
5
+ declare function SearchInput({ value, onClear, ...props }: ComponentProps<typeof Input> & {
6
+ value: string;
7
+ onClear: () => void;
8
+ }): react_jsx_runtime.JSX.Element;
9
+ declare function MarketplaceSearch(): react_jsx_runtime.JSX.Element;
10
+
11
+ export { MarketplaceSearch, SearchInput };
@@ -0,0 +1,140 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import Image from "./image.js";
6
+ import Link from "./link.js";
7
+ import { Search, X } from "lucide-react";
8
+ import { assetHref, collectionHref } from "@medialane/sdk";
9
+ import { Input } from "./input.js";
10
+ import { useMedialaneClient } from "../utils/use-medialane-client.js";
11
+ import { useDebouncedValue } from "../utils/use-debounced-value.js";
12
+ import { useImageUrl } from "../utils/use-image-url.js";
13
+ function SearchInput({ value, onClear, ...props }) {
14
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
15
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
16
+ /* @__PURE__ */ jsx(Input, { value, className: "pl-9 pr-9", ...props }),
17
+ value && /* @__PURE__ */ jsx(
18
+ "button",
19
+ {
20
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
21
+ onClick: onClear,
22
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
23
+ }
24
+ )
25
+ ] });
26
+ }
27
+ function MarketplaceSearch() {
28
+ const resolveImage = useImageUrl();
29
+ const client = useMedialaneClient();
30
+ const router = useRouter();
31
+ const [query, setQuery] = useState("");
32
+ const [results, setResults] = useState(null);
33
+ const [open, setOpen] = useState(false);
34
+ const debouncedQuery = useDebouncedValue(query.trim(), 300);
35
+ useEffect(() => {
36
+ if (!debouncedQuery) {
37
+ setResults(null);
38
+ setOpen(false);
39
+ return;
40
+ }
41
+ let current = true;
42
+ client.api.search(debouncedQuery, 8).then((res) => {
43
+ if (!current) return;
44
+ setResults(res.data);
45
+ setOpen(true);
46
+ }).catch(() => {
47
+ if (current) setResults(null);
48
+ });
49
+ return () => {
50
+ current = false;
51
+ };
52
+ }, [debouncedQuery, client]);
53
+ const handleKeyDown = (e) => {
54
+ if (e.key === "Enter" && query.trim()) {
55
+ setOpen(false);
56
+ router.push(`/search?q=${encodeURIComponent(query.trim())}`);
57
+ }
58
+ };
59
+ const hasResults = results && ((results.tokens?.length ?? 0) > 0 || (results.collections?.length ?? 0) > 0);
60
+ return /* @__PURE__ */ jsxs("div", { className: "relative w-full sm:max-w-sm", children: [
61
+ /* @__PURE__ */ jsx(
62
+ SearchInput,
63
+ {
64
+ placeholder: "Search creative works",
65
+ value: query,
66
+ onChange: (e) => setQuery(e.target.value),
67
+ onKeyDown: handleKeyDown,
68
+ onFocus: () => hasResults && setOpen(true),
69
+ onBlur: () => setOpen(false),
70
+ onClear: () => {
71
+ setQuery("");
72
+ setResults(null);
73
+ setOpen(false);
74
+ }
75
+ }
76
+ ),
77
+ open && hasResults && /* @__PURE__ */ jsxs(
78
+ "div",
79
+ {
80
+ onMouseDown: (e) => e.preventDefault(),
81
+ className: "absolute top-full left-0 right-0 mt-1 bg-popover border border-border rounded-xl shadow-lg z-50 overflow-hidden",
82
+ children: [
83
+ (results.tokens?.length ?? 0) > 0 && /* @__PURE__ */ jsxs("div", { children: [
84
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30", children: "Tokens" }),
85
+ results.tokens.map((t) => {
86
+ const img = resolveImage(t.image);
87
+ return /* @__PURE__ */ jsxs(
88
+ Link,
89
+ {
90
+ href: assetHref("STARKNET", t.contractAddress, t.tokenId),
91
+ className: "flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors",
92
+ children: [
93
+ /* @__PURE__ */ jsx("div", { className: "relative h-8 w-8 rounded-lg bg-muted shrink-0 overflow-hidden", children: img && /* @__PURE__ */ jsx(Image, { src: img, alt: "", fill: true, className: "object-cover", unoptimized: true }) }),
94
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
95
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: t.name ?? `Token #${t.tokenId}` }),
96
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground tabular-nums truncate", children: [
97
+ t.contractAddress.slice(0, 14),
98
+ "\u2026"
99
+ ] })
100
+ ] })
101
+ ]
102
+ },
103
+ `${t.contractAddress}-${t.tokenId}`
104
+ );
105
+ })
106
+ ] }),
107
+ (results.collections?.length ?? 0) > 0 && /* @__PURE__ */ jsxs("div", { children: [
108
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30", children: "Collections" }),
109
+ results.collections.map((c) => {
110
+ const imgUrl = resolveImage(c.image);
111
+ return /* @__PURE__ */ jsxs(
112
+ Link,
113
+ {
114
+ href: collectionHref("STARKNET", c.contractAddress),
115
+ className: "flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors",
116
+ children: [
117
+ /* @__PURE__ */ jsx("div", { className: "relative h-8 w-8 rounded-lg bg-gradient-to-br from-primary/20 to-brand-purple/20 flex items-center justify-center text-xs font-bold shrink-0 overflow-hidden", children: imgUrl ? /* @__PURE__ */ jsx(Image, { src: imgUrl, alt: "", fill: true, className: "object-cover", unoptimized: true }) : /* @__PURE__ */ jsx("span", { children: c.name?.charAt(0) ?? "?" }) }),
118
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
119
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: c.name }),
120
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground tabular-nums truncate", children: [
121
+ c.contractAddress.slice(0, 14),
122
+ "\u2026"
123
+ ] })
124
+ ] })
125
+ ]
126
+ },
127
+ c.contractAddress
128
+ );
129
+ })
130
+ ] })
131
+ ]
132
+ }
133
+ )
134
+ ] });
135
+ }
136
+ export {
137
+ MarketplaceSearch,
138
+ SearchInput
139
+ };
140
+ //# sourceMappingURL=marketplace-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/marketplace-search.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState, type ComponentProps } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport Image from \"./image.js\";\nimport Link from \"./link.js\";\nimport { Search, X } from \"lucide-react\";\nimport { assetHref, collectionHref, type ApiSearchResult } from \"@medialane/sdk\";\nimport { Input } from \"./input.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useDebouncedValue } from \"../utils/use-debounced-value.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\nexport function SearchInput({ value, onClear, ...props }: ComponentProps<typeof Input> & { value: string; onClear: () => void }) {\n return (\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input value={value} className=\"pl-9 pr-9\" {...props} />\n {value && (\n <button\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n onClick={onClear}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n );\n}\n\nexport function MarketplaceSearch() {\n const resolveImage = useImageUrl();\n const client = useMedialaneClient();\n const router = useRouter();\n const [query, setQuery] = useState(\"\");\n const [results, setResults] = useState<ApiSearchResult | null>(null);\n const [open, setOpen] = useState(false);\n const debouncedQuery = useDebouncedValue(query.trim(), 300);\n\n useEffect(() => {\n if (!debouncedQuery) {\n setResults(null);\n setOpen(false);\n return;\n }\n let current = true;\n client.api\n .search(debouncedQuery, 8)\n .then((res) => {\n if (!current) return;\n setResults(res.data);\n setOpen(true);\n })\n .catch(() => {\n if (current) setResults(null);\n });\n return () => {\n current = false;\n };\n }, [debouncedQuery, client]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && query.trim()) {\n setOpen(false);\n router.push(`/search?q=${encodeURIComponent(query.trim())}`);\n }\n };\n\n const hasResults =\n results && ((results.tokens?.length ?? 0) > 0 || (results.collections?.length ?? 0) > 0);\n\n return (\n <div className=\"relative w-full sm:max-w-sm\">\n <SearchInput\n placeholder=\"Search creative works\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n onFocus={() => hasResults && setOpen(true)}\n onBlur={() => setOpen(false)}\n onClear={() => { setQuery(\"\"); setResults(null); setOpen(false); }}\n />\n\n {open && hasResults && (\n <div\n onMouseDown={(e) => e.preventDefault()}\n className=\"absolute top-full left-0 right-0 mt-1 bg-popover border border-border rounded-xl shadow-lg z-50 overflow-hidden\"\n >\n {(results.tokens?.length ?? 0) > 0 && (\n <div>\n <div className=\"px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30\">\n Tokens\n </div>\n {results.tokens!.map((t) => {\n const img = resolveImage(t.image);\n return (\n <Link\n key={`${t.contractAddress}-${t.tokenId}`}\n href={assetHref(\"STARKNET\", t.contractAddress, t.tokenId)}\n className=\"flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"relative h-8 w-8 rounded-lg bg-muted shrink-0 overflow-hidden\">\n {img && <Image src={img} alt=\"\" fill className=\"object-cover\" unoptimized />}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">{t.name ?? `Token #${t.tokenId}`}</p>\n <p className=\"text-xs text-muted-foreground tabular-nums truncate\">{t.contractAddress.slice(0, 14)}…</p>\n </div>\n </Link>\n );\n })}\n </div>\n )}\n {(results.collections?.length ?? 0) > 0 && (\n <div>\n <div className=\"px-3 py-1.5 text-2xs font-semibold text-muted-foreground bg-muted/30\">\n Collections\n </div>\n {results.collections!.map((c) => {\n const imgUrl = resolveImage(c.image);\n return (\n <Link\n key={c.contractAddress}\n href={collectionHref(\"STARKNET\", c.contractAddress)}\n className=\"flex items-center gap-3 px-3 py-2.5 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"relative h-8 w-8 rounded-lg bg-gradient-to-br from-primary/20 to-brand-purple/20 flex items-center justify-center text-xs font-bold shrink-0 overflow-hidden\">\n {imgUrl ? (\n <Image src={imgUrl} alt=\"\" fill className=\"object-cover\" unoptimized />\n ) : (\n <span>{c.name?.charAt(0) ?? \"?\"}</span>\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">{c.name}</p>\n <p className=\"text-xs text-muted-foreground tabular-nums truncate\">{c.contractAddress.slice(0, 14)}…</p>\n </div>\n </Link>\n );\n })}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}