@medialane/ui 0.76.0 → 0.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/components/asset-marketplace-panel.cjs +48 -0
  2. package/dist/components/asset-marketplace-panel.cjs.map +1 -1
  3. package/dist/components/asset-marketplace-panel.d.cts +7 -1
  4. package/dist/components/asset-marketplace-panel.d.ts +7 -1
  5. package/dist/components/asset-marketplace-panel.js +49 -0
  6. package/dist/components/asset-marketplace-panel.js.map +1 -1
  7. package/dist/components/asset-picker-cell.cjs +69 -0
  8. package/dist/components/asset-picker-cell.cjs.map +1 -0
  9. package/dist/components/asset-picker-cell.d.cts +15 -0
  10. package/dist/components/asset-picker-cell.d.ts +15 -0
  11. package/dist/components/asset-picker-cell.js +34 -0
  12. package/dist/components/asset-picker-cell.js.map +1 -0
  13. package/dist/components/asset-picker.cjs +10 -33
  14. package/dist/components/asset-picker.cjs.map +1 -1
  15. package/dist/components/asset-picker.js +11 -24
  16. package/dist/components/asset-picker.js.map +1 -1
  17. package/dist/components/asset-search-picker.cjs +100 -0
  18. package/dist/components/asset-search-picker.cjs.map +1 -0
  19. package/dist/components/asset-search-picker.d.cts +15 -0
  20. package/dist/components/asset-search-picker.d.ts +15 -0
  21. package/dist/components/asset-search-picker.js +76 -0
  22. package/dist/components/asset-search-picker.js.map +1 -0
  23. package/dist/components/license-terms-builder.cjs +287 -39
  24. package/dist/components/license-terms-builder.cjs.map +1 -1
  25. package/dist/components/license-terms-builder.d.cts +30 -6
  26. package/dist/components/license-terms-builder.d.ts +30 -6
  27. package/dist/components/license-terms-builder.js +284 -38
  28. package/dist/components/license-terms-builder.js.map +1 -1
  29. package/dist/index.cjs +7 -0
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +2 -1
  32. package/dist/index.d.ts +2 -1
  33. package/dist/index.js +5 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/utils/portfolio-counts.cjs +2 -2
  36. package/dist/utils/portfolio-counts.cjs.map +1 -1
  37. package/dist/utils/portfolio-counts.d.cts +6 -1
  38. package/dist/utils/portfolio-counts.d.ts +6 -1
  39. package/dist/utils/portfolio-counts.js +2 -2
  40. package/dist/utils/portfolio-counts.js.map +1 -1
  41. package/package.json +1 -1
@@ -1,10 +1,8 @@
1
1
  "use strict";
2
2
  "use client";
3
- var __create = Object.create;
4
3
  var __defProp = Object.defineProperty;
5
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
7
  var __export = (target, all) => {
10
8
  for (var name in all)
@@ -18,14 +16,6 @@ var __copyProps = (to, from, except, desc) => {
18
16
  }
19
17
  return to;
20
18
  };
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
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
20
  var asset_picker_exports = {};
31
21
  __export(asset_picker_exports, {
@@ -34,10 +24,9 @@ __export(asset_picker_exports, {
34
24
  module.exports = __toCommonJS(asset_picker_exports);
35
25
  var import_jsx_runtime = require("react/jsx-runtime");
36
26
  var import_react = require("react");
37
- var import_image = __toESM(require("next/image"), 1);
38
27
  var import_lucide_react = require("lucide-react");
39
28
  var import_cn = require("../utils/cn.js");
40
- const isSame = (a, b) => !!b && a.contractAddress === b.contractAddress && a.tokenId === b.tokenId;
29
+ var import_asset_picker_cell = require("./asset-picker-cell.js");
41
30
  function AssetPicker({
42
31
  assets,
43
32
  isLoading,
@@ -72,27 +61,15 @@ function AssetPicker({
72
61
  }
73
62
  )
74
63
  ] }) : null,
75
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5", children: filtered.map((asset) => {
76
- const active = isSame(asset, selected);
77
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
78
- "button",
79
- {
80
- type: "button",
81
- onClick: () => onSelect(asset),
82
- "aria-pressed": active,
83
- className: (0, import_cn.cn)(
84
- "group relative aspect-square rounded-xl overflow-hidden border-2 transition-colors text-left",
85
- active ? "border-brand-purple" : "border-border/50 active:border-border sm:hover:border-border"
86
- ),
87
- children: [
88
- asset.image ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_image.default, { src: asset.image, alt: asset.name, fill: true, sizes: "120px", className: "object-cover" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-full w-full flex items-center justify-center bg-muted/60", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ImageIcon, { className: "h-5 w-5 text-muted-foreground/50" }) }),
89
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent px-1.5 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "truncate text-[11px] font-medium text-white", children: asset.name }) }),
90
- active ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute top-1.5 right-1.5 h-5 w-5 rounded-full bg-brand-purple flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Check, { className: "h-3 w-3 text-white" }) }) : null
91
- ]
92
- },
93
- `${asset.contractAddress}-${asset.tokenId}`
94
- );
95
- }) }),
64
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5", children: filtered.map((asset) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
65
+ import_asset_picker_cell.AssetPickerCell,
66
+ {
67
+ asset,
68
+ active: (0, import_asset_picker_cell.isSameAsset)(asset, selected),
69
+ onSelect
70
+ },
71
+ `${asset.contractAddress}-${asset.tokenId}`
72
+ )) }),
96
73
  query && filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground text-center py-4", children: [
97
74
  'No assets match "',
98
75
  query,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/asset-picker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Image from \"next/image\";\nimport { Check, ImageIcon, Search } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface OwnedAsset {\n contractAddress: string;\n tokenId: string;\n name: string;\n image: string | null;\n}\n\nexport interface AssetPickerProps {\n /** The caller's already-fetched, already-resolved asset list — this\n * component takes data, it doesn't fetch it. Plug in `useTokensByOwner`\n * (or an app's equivalent) and map to `OwnedAsset` at the call site. */\n assets: OwnedAsset[];\n isLoading: boolean;\n selected: OwnedAsset | null;\n onSelect: (asset: OwnedAsset) => void;\n /** Shown in the empty state when the wallet owns nothing yet. */\n emptyStateHref?: string;\n emptyStateLabel?: string;\n className?: string;\n}\n\nconst isSame = (a: OwnedAsset, b: OwnedAsset | null) =>\n !!b && a.contractAddress === b.contractAddress && a.tokenId === b.tokenId;\n\nexport function AssetPicker({\n assets, isLoading, selected, onSelect, emptyStateHref, emptyStateLabel, className,\n}: AssetPickerProps) {\n const [query, setQuery] = useState(\"\");\n const filtered = query\n ? assets.filter((a) => a.name.toLowerCase().includes(query.toLowerCase()))\n : assets;\n\n if (isLoading) {\n return (\n <div className={cn(\"grid grid-cols-3 sm:grid-cols-4 gap-2.5\", className)}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"aspect-square rounded-xl bg-muted/60 animate-pulse\" />\n ))}\n </div>\n );\n }\n\n if (assets.length === 0) {\n return (\n <div className={cn(\"rounded-xl border border-dashed border-border/60 p-6 text-center text-sm text-muted-foreground\", className)}>\n You don&apos;t own any assets yet.\n {emptyStateHref ? (\n <a href={emptyStateHref} className=\"ml-1 font-semibold text-foreground underline underline-offset-2\">\n {emptyStateLabel ?? \"Create one\"}\n </a>\n ) : null}\n </div>\n );\n }\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {assets.length > 8 ? (\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search your assets\"\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n </div>\n ) : null}\n\n <div className=\"grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5\">\n {filtered.map((asset) => {\n const active = isSame(asset, selected);\n return (\n <button\n key={`${asset.contractAddress}-${asset.tokenId}`}\n type=\"button\"\n onClick={() => onSelect(asset)}\n aria-pressed={active}\n className={cn(\n \"group relative aspect-square rounded-xl overflow-hidden border-2 transition-colors text-left\",\n active ? \"border-brand-purple\" : \"border-border/50 active:border-border sm:hover:border-border\",\n )}\n >\n {asset.image ? (\n <Image src={asset.image} alt={asset.name} fill sizes=\"120px\" className=\"object-cover\" />\n ) : (\n <div className=\"h-full w-full flex items-center justify-center bg-muted/60\">\n <ImageIcon className=\"h-5 w-5 text-muted-foreground/50\" />\n </div>\n )}\n <div className=\"absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent px-1.5 py-1\">\n <p className=\"truncate text-[11px] font-medium text-white\">{asset.name}</p>\n </div>\n {active ? (\n <div className=\"absolute top-1.5 right-1.5 h-5 w-5 rounded-full bg-brand-purple flex items-center justify-center\">\n <Check className=\"h-3 w-3 text-white\" />\n </div>\n ) : null}\n </button>\n );\n })}\n </div>\n\n {query && filtered.length === 0 ? (\n <p className=\"text-xs text-muted-foreground text-center py-4\">No assets match &quot;{query}&quot;.</p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2CU;AAzCV,mBAAyB;AACzB,mBAAkB;AAClB,0BAAyC;AACzC,gBAAmB;AAuBnB,MAAM,SAAS,CAAC,GAAe,MAC7B,CAAC,CAAC,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,YAAY,EAAE;AAE7D,SAAS,YAAY;AAAA,EAC1B;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAgB;AAAA,EAAiB;AAC1E,GAAqB;AACnB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,WAAW,QACb,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,CAAC,IACvE;AAEJ,MAAI,WAAW;AACb,WACE,4CAAC,SAAI,eAAW,cAAG,2CAA2C,SAAS,GACpE,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,4CAAC,SAAY,WAAU,wDAAb,CAAkE,CAC7E,GACH;AAAA,EAEJ;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WACE,6CAAC,SAAI,eAAW,cAAG,kGAAkG,SAAS,GAAG;AAAA;AAAA,MAE9H,iBACC,4CAAC,OAAE,MAAM,gBAAgB,WAAU,mEAChC,6BAAmB,cACtB,IACE;AAAA,OACN;AAAA,EAEJ;AAEA,SACE,6CAAC,SAAI,eAAW,cAAG,aAAa,SAAS,GACtC;AAAA,WAAO,SAAS,IACf,6CAAC,SAAI,WAAU,YACb;AAAA,kDAAC,8BAAO,WAAU,8FAA6F;AAAA,MAC/G;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,aAAY;AAAA,UACZ,WAAU;AAAA;AAAA,MACZ;AAAA,OACF,IACE;AAAA,IAEJ,4CAAC,SAAI,WAAU,2EACZ,mBAAS,IAAI,CAAC,UAAU;AACvB,YAAM,SAAS,OAAO,OAAO,QAAQ;AACrC,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,SAAS,MAAM,SAAS,KAAK;AAAA,UAC7B,gBAAc;AAAA,UACd,eAAW;AAAA,YACT;AAAA,YACA,SAAS,wBAAwB;AAAA,UACnC;AAAA,UAEC;AAAA,kBAAM,QACL,4CAAC,aAAAA,SAAA,EAAM,KAAK,MAAM,OAAO,KAAK,MAAM,MAAM,MAAI,MAAC,OAAM,SAAQ,WAAU,gBAAe,IAEtF,4CAAC,SAAI,WAAU,8DACb,sDAAC,iCAAU,WAAU,oCAAmC,GAC1D;AAAA,YAEF,4CAAC,SAAI,WAAU,yFACb,sDAAC,OAAE,WAAU,+CAA+C,gBAAM,MAAK,GACzE;AAAA,YACC,SACC,4CAAC,SAAI,WAAU,oGACb,sDAAC,6BAAM,WAAU,sBAAqB,GACxC,IACE;AAAA;AAAA;AAAA,QAvBC,GAAG,MAAM,eAAe,IAAI,MAAM,OAAO;AAAA,MAwBhD;AAAA,IAEJ,CAAC,GACH;AAAA,IAEC,SAAS,SAAS,WAAW,IAC5B,6CAAC,OAAE,WAAU,kDAAiD;AAAA;AAAA,MAAuB;AAAA,MAAM;AAAA,OAAO,IAChG;AAAA,KACN;AAEJ;","names":["Image"]}
1
+ {"version":3,"sources":["../../src/components/asset-picker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { Search } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { AssetPickerCell, isSameAsset } from \"./asset-picker-cell.js\";\n\nexport interface OwnedAsset {\n contractAddress: string;\n tokenId: string;\n name: string;\n image: string | null;\n}\n\nexport interface AssetPickerProps {\n /** The caller's already-fetched, already-resolved asset list — this\n * component takes data, it doesn't fetch it. Plug in `useTokensByOwner`\n * (or an app's equivalent) and map to `OwnedAsset` at the call site. */\n assets: OwnedAsset[];\n isLoading: boolean;\n selected: OwnedAsset | null;\n onSelect: (asset: OwnedAsset) => void;\n /** Shown in the empty state when the wallet owns nothing yet. */\n emptyStateHref?: string;\n emptyStateLabel?: string;\n className?: string;\n}\n\nexport function AssetPicker({\n assets, isLoading, selected, onSelect, emptyStateHref, emptyStateLabel, className,\n}: AssetPickerProps) {\n const [query, setQuery] = useState(\"\");\n const filtered = query\n ? assets.filter((a) => a.name.toLowerCase().includes(query.toLowerCase()))\n : assets;\n\n if (isLoading) {\n return (\n <div className={cn(\"grid grid-cols-3 sm:grid-cols-4 gap-2.5\", className)}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"aspect-square rounded-xl bg-muted/60 animate-pulse\" />\n ))}\n </div>\n );\n }\n\n if (assets.length === 0) {\n return (\n <div className={cn(\"rounded-xl border border-dashed border-border/60 p-6 text-center text-sm text-muted-foreground\", className)}>\n You don&apos;t own any assets yet.\n {emptyStateHref ? (\n <a href={emptyStateHref} className=\"ml-1 font-semibold text-foreground underline underline-offset-2\">\n {emptyStateLabel ?? \"Create one\"}\n </a>\n ) : null}\n </div>\n );\n }\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {assets.length > 8 ? (\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search your assets\"\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n </div>\n ) : null}\n\n <div className=\"grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5\">\n {filtered.map((asset) => (\n <AssetPickerCell\n key={`${asset.contractAddress}-${asset.tokenId}`}\n asset={asset}\n active={isSameAsset(asset, selected)}\n onSelect={onSelect}\n />\n ))}\n </div>\n\n {query && filtered.length === 0 ? (\n <p className=\"text-xs text-muted-foreground text-center py-4\">No assets match &quot;{query}&quot;.</p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCU;AAtCV,mBAAyB;AACzB,0BAAuB;AACvB,gBAAmB;AACnB,+BAA6C;AAuBtC,SAAS,YAAY;AAAA,EAC1B;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAgB;AAAA,EAAiB;AAC1E,GAAqB;AACnB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,WAAW,QACb,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,CAAC,IACvE;AAEJ,MAAI,WAAW;AACb,WACE,4CAAC,SAAI,eAAW,cAAG,2CAA2C,SAAS,GACpE,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,4CAAC,SAAY,WAAU,wDAAb,CAAkE,CAC7E,GACH;AAAA,EAEJ;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WACE,6CAAC,SAAI,eAAW,cAAG,kGAAkG,SAAS,GAAG;AAAA;AAAA,MAE9H,iBACC,4CAAC,OAAE,MAAM,gBAAgB,WAAU,mEAChC,6BAAmB,cACtB,IACE;AAAA,OACN;AAAA,EAEJ;AAEA,SACE,6CAAC,SAAI,eAAW,cAAG,aAAa,SAAS,GACtC;AAAA,WAAO,SAAS,IACf,6CAAC,SAAI,WAAU,YACb;AAAA,kDAAC,8BAAO,WAAU,8FAA6F;AAAA,MAC/G;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,aAAY;AAAA,UACZ,WAAU;AAAA;AAAA,MACZ;AAAA,OACF,IACE;AAAA,IAEJ,4CAAC,SAAI,WAAU,2EACZ,mBAAS,IAAI,CAAC,UACb;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,YAAQ,sCAAY,OAAO,QAAQ;AAAA,QACnC;AAAA;AAAA,MAHK,GAAG,MAAM,eAAe,IAAI,MAAM,OAAO;AAAA,IAIhD,CACD,GACH;AAAA,IAEC,SAAS,SAAS,WAAW,IAC5B,6CAAC,OAAE,WAAU,kDAAiD;AAAA;AAAA,MAAuB;AAAA,MAAM;AAAA,OAAO,IAChG;AAAA,KACN;AAEJ;","names":[]}
@@ -1,10 +1,9 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import Image from "next/image";
5
- import { Check, ImageIcon, Search } from "lucide-react";
4
+ import { Search } from "lucide-react";
6
5
  import { cn } from "../utils/cn.js";
7
- const isSame = (a, b) => !!b && a.contractAddress === b.contractAddress && a.tokenId === b.tokenId;
6
+ import { AssetPickerCell, isSameAsset } from "./asset-picker-cell.js";
8
7
  function AssetPicker({
9
8
  assets,
10
9
  isLoading,
@@ -39,27 +38,15 @@ function AssetPicker({
39
38
  }
40
39
  )
41
40
  ] }) : null,
42
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5", children: filtered.map((asset) => {
43
- const active = isSame(asset, selected);
44
- return /* @__PURE__ */ jsxs(
45
- "button",
46
- {
47
- type: "button",
48
- onClick: () => onSelect(asset),
49
- "aria-pressed": active,
50
- className: cn(
51
- "group relative aspect-square rounded-xl overflow-hidden border-2 transition-colors text-left",
52
- active ? "border-brand-purple" : "border-border/50 active:border-border sm:hover:border-border"
53
- ),
54
- children: [
55
- asset.image ? /* @__PURE__ */ jsx(Image, { src: asset.image, alt: asset.name, fill: true, sizes: "120px", className: "object-cover" }) : /* @__PURE__ */ jsx("div", { className: "h-full w-full flex items-center justify-center bg-muted/60", children: /* @__PURE__ */ jsx(ImageIcon, { className: "h-5 w-5 text-muted-foreground/50" }) }),
56
- /* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent px-1.5 py-1", children: /* @__PURE__ */ jsx("p", { className: "truncate text-[11px] font-medium text-white", children: asset.name }) }),
57
- active ? /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 right-1.5 h-5 w-5 rounded-full bg-brand-purple flex items-center justify-center", children: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3 text-white" }) }) : null
58
- ]
59
- },
60
- `${asset.contractAddress}-${asset.tokenId}`
61
- );
62
- }) }),
41
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5", children: filtered.map((asset) => /* @__PURE__ */ jsx(
42
+ AssetPickerCell,
43
+ {
44
+ asset,
45
+ active: isSameAsset(asset, selected),
46
+ onSelect
47
+ },
48
+ `${asset.contractAddress}-${asset.tokenId}`
49
+ )) }),
63
50
  query && filtered.length === 0 ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground text-center py-4", children: [
64
51
  'No assets match "',
65
52
  query,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/asset-picker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Image from \"next/image\";\nimport { Check, ImageIcon, Search } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface OwnedAsset {\n contractAddress: string;\n tokenId: string;\n name: string;\n image: string | null;\n}\n\nexport interface AssetPickerProps {\n /** The caller's already-fetched, already-resolved asset list — this\n * component takes data, it doesn't fetch it. Plug in `useTokensByOwner`\n * (or an app's equivalent) and map to `OwnedAsset` at the call site. */\n assets: OwnedAsset[];\n isLoading: boolean;\n selected: OwnedAsset | null;\n onSelect: (asset: OwnedAsset) => void;\n /** Shown in the empty state when the wallet owns nothing yet. */\n emptyStateHref?: string;\n emptyStateLabel?: string;\n className?: string;\n}\n\nconst isSame = (a: OwnedAsset, b: OwnedAsset | null) =>\n !!b && a.contractAddress === b.contractAddress && a.tokenId === b.tokenId;\n\nexport function AssetPicker({\n assets, isLoading, selected, onSelect, emptyStateHref, emptyStateLabel, className,\n}: AssetPickerProps) {\n const [query, setQuery] = useState(\"\");\n const filtered = query\n ? assets.filter((a) => a.name.toLowerCase().includes(query.toLowerCase()))\n : assets;\n\n if (isLoading) {\n return (\n <div className={cn(\"grid grid-cols-3 sm:grid-cols-4 gap-2.5\", className)}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"aspect-square rounded-xl bg-muted/60 animate-pulse\" />\n ))}\n </div>\n );\n }\n\n if (assets.length === 0) {\n return (\n <div className={cn(\"rounded-xl border border-dashed border-border/60 p-6 text-center text-sm text-muted-foreground\", className)}>\n You don&apos;t own any assets yet.\n {emptyStateHref ? (\n <a href={emptyStateHref} className=\"ml-1 font-semibold text-foreground underline underline-offset-2\">\n {emptyStateLabel ?? \"Create one\"}\n </a>\n ) : null}\n </div>\n );\n }\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {assets.length > 8 ? (\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search your assets\"\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n </div>\n ) : null}\n\n <div className=\"grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5\">\n {filtered.map((asset) => {\n const active = isSame(asset, selected);\n return (\n <button\n key={`${asset.contractAddress}-${asset.tokenId}`}\n type=\"button\"\n onClick={() => onSelect(asset)}\n aria-pressed={active}\n className={cn(\n \"group relative aspect-square rounded-xl overflow-hidden border-2 transition-colors text-left\",\n active ? \"border-brand-purple\" : \"border-border/50 active:border-border sm:hover:border-border\",\n )}\n >\n {asset.image ? (\n <Image src={asset.image} alt={asset.name} fill sizes=\"120px\" className=\"object-cover\" />\n ) : (\n <div className=\"h-full w-full flex items-center justify-center bg-muted/60\">\n <ImageIcon className=\"h-5 w-5 text-muted-foreground/50\" />\n </div>\n )}\n <div className=\"absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent px-1.5 py-1\">\n <p className=\"truncate text-[11px] font-medium text-white\">{asset.name}</p>\n </div>\n {active ? (\n <div className=\"absolute top-1.5 right-1.5 h-5 w-5 rounded-full bg-brand-purple flex items-center justify-center\">\n <Check className=\"h-3 w-3 text-white\" />\n </div>\n ) : null}\n </button>\n );\n })}\n </div>\n\n {query && filtered.length === 0 ? (\n <p className=\"text-xs text-muted-foreground text-center py-4\">No assets match &quot;{query}&quot;.</p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";AA2CU,cAQJ,YARI;AAzCV,SAAS,gBAAgB;AACzB,OAAO,WAAW;AAClB,SAAS,OAAO,WAAW,cAAc;AACzC,SAAS,UAAU;AAuBnB,MAAM,SAAS,CAAC,GAAe,MAC7B,CAAC,CAAC,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,YAAY,EAAE;AAE7D,SAAS,YAAY;AAAA,EAC1B;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAgB;AAAA,EAAiB;AAC1E,GAAqB;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,WAAW,QACb,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,CAAC,IACvE;AAEJ,MAAI,WAAW;AACb,WACE,oBAAC,SAAI,WAAW,GAAG,2CAA2C,SAAS,GACpE,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,oBAAC,SAAY,WAAU,wDAAb,CAAkE,CAC7E,GACH;AAAA,EAEJ;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WACE,qBAAC,SAAI,WAAW,GAAG,kGAAkG,SAAS,GAAG;AAAA;AAAA,MAE9H,iBACC,oBAAC,OAAE,MAAM,gBAAgB,WAAU,mEAChC,6BAAmB,cACtB,IACE;AAAA,OACN;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GACtC;AAAA,WAAO,SAAS,IACf,qBAAC,SAAI,WAAU,YACb;AAAA,0BAAC,UAAO,WAAU,8FAA6F;AAAA,MAC/G;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,aAAY;AAAA,UACZ,WAAU;AAAA;AAAA,MACZ;AAAA,OACF,IACE;AAAA,IAEJ,oBAAC,SAAI,WAAU,2EACZ,mBAAS,IAAI,CAAC,UAAU;AACvB,YAAM,SAAS,OAAO,OAAO,QAAQ;AACrC,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,SAAS,MAAM,SAAS,KAAK;AAAA,UAC7B,gBAAc;AAAA,UACd,WAAW;AAAA,YACT;AAAA,YACA,SAAS,wBAAwB;AAAA,UACnC;AAAA,UAEC;AAAA,kBAAM,QACL,oBAAC,SAAM,KAAK,MAAM,OAAO,KAAK,MAAM,MAAM,MAAI,MAAC,OAAM,SAAQ,WAAU,gBAAe,IAEtF,oBAAC,SAAI,WAAU,8DACb,8BAAC,aAAU,WAAU,oCAAmC,GAC1D;AAAA,YAEF,oBAAC,SAAI,WAAU,yFACb,8BAAC,OAAE,WAAU,+CAA+C,gBAAM,MAAK,GACzE;AAAA,YACC,SACC,oBAAC,SAAI,WAAU,oGACb,8BAAC,SAAM,WAAU,sBAAqB,GACxC,IACE;AAAA;AAAA;AAAA,QAvBC,GAAG,MAAM,eAAe,IAAI,MAAM,OAAO;AAAA,MAwBhD;AAAA,IAEJ,CAAC,GACH;AAAA,IAEC,SAAS,SAAS,WAAW,IAC5B,qBAAC,OAAE,WAAU,kDAAiD;AAAA;AAAA,MAAuB;AAAA,MAAM;AAAA,OAAO,IAChG;AAAA,KACN;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/asset-picker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { Search } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { AssetPickerCell, isSameAsset } from \"./asset-picker-cell.js\";\n\nexport interface OwnedAsset {\n contractAddress: string;\n tokenId: string;\n name: string;\n image: string | null;\n}\n\nexport interface AssetPickerProps {\n /** The caller's already-fetched, already-resolved asset list — this\n * component takes data, it doesn't fetch it. Plug in `useTokensByOwner`\n * (or an app's equivalent) and map to `OwnedAsset` at the call site. */\n assets: OwnedAsset[];\n isLoading: boolean;\n selected: OwnedAsset | null;\n onSelect: (asset: OwnedAsset) => void;\n /** Shown in the empty state when the wallet owns nothing yet. */\n emptyStateHref?: string;\n emptyStateLabel?: string;\n className?: string;\n}\n\nexport function AssetPicker({\n assets, isLoading, selected, onSelect, emptyStateHref, emptyStateLabel, className,\n}: AssetPickerProps) {\n const [query, setQuery] = useState(\"\");\n const filtered = query\n ? assets.filter((a) => a.name.toLowerCase().includes(query.toLowerCase()))\n : assets;\n\n if (isLoading) {\n return (\n <div className={cn(\"grid grid-cols-3 sm:grid-cols-4 gap-2.5\", className)}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"aspect-square rounded-xl bg-muted/60 animate-pulse\" />\n ))}\n </div>\n );\n }\n\n if (assets.length === 0) {\n return (\n <div className={cn(\"rounded-xl border border-dashed border-border/60 p-6 text-center text-sm text-muted-foreground\", className)}>\n You don&apos;t own any assets yet.\n {emptyStateHref ? (\n <a href={emptyStateHref} className=\"ml-1 font-semibold text-foreground underline underline-offset-2\">\n {emptyStateLabel ?? \"Create one\"}\n </a>\n ) : null}\n </div>\n );\n }\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {assets.length > 8 ? (\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search your assets\"\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n </div>\n ) : null}\n\n <div className=\"grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5\">\n {filtered.map((asset) => (\n <AssetPickerCell\n key={`${asset.contractAddress}-${asset.tokenId}`}\n asset={asset}\n active={isSameAsset(asset, selected)}\n onSelect={onSelect}\n />\n ))}\n </div>\n\n {query && filtered.length === 0 ? (\n <p className=\"text-xs text-muted-foreground text-center py-4\">No assets match &quot;{query}&quot;.</p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";AAwCU,cAQJ,YARI;AAtCV,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,UAAU;AACnB,SAAS,iBAAiB,mBAAmB;AAuBtC,SAAS,YAAY;AAAA,EAC1B;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAgB;AAAA,EAAiB;AAC1E,GAAqB;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,WAAW,QACb,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,CAAC,IACvE;AAEJ,MAAI,WAAW;AACb,WACE,oBAAC,SAAI,WAAW,GAAG,2CAA2C,SAAS,GACpE,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,oBAAC,SAAY,WAAU,wDAAb,CAAkE,CAC7E,GACH;AAAA,EAEJ;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WACE,qBAAC,SAAI,WAAW,GAAG,kGAAkG,SAAS,GAAG;AAAA;AAAA,MAE9H,iBACC,oBAAC,OAAE,MAAM,gBAAgB,WAAU,mEAChC,6BAAmB,cACtB,IACE;AAAA,OACN;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GACtC;AAAA,WAAO,SAAS,IACf,qBAAC,SAAI,WAAU,YACb;AAAA,0BAAC,UAAO,WAAU,8FAA6F;AAAA,MAC/G;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,aAAY;AAAA,UACZ,WAAU;AAAA;AAAA,MACZ;AAAA,OACF,IACE;AAAA,IAEJ,oBAAC,SAAI,WAAU,2EACZ,mBAAS,IAAI,CAAC,UACb;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,QAAQ,YAAY,OAAO,QAAQ;AAAA,QACnC;AAAA;AAAA,MAHK,GAAG,MAAM,eAAe,IAAI,MAAM,OAAO;AAAA,IAIhD,CACD,GACH;AAAA,IAEC,SAAS,SAAS,WAAW,IAC5B,qBAAC,OAAE,WAAU,kDAAiD;AAAA;AAAA,MAAuB;AAAA,MAAM;AAAA,OAAO,IAChG;AAAA,KACN;AAEJ;","names":[]}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var asset_search_picker_exports = {};
21
+ __export(asset_search_picker_exports, {
22
+ AssetSearchPicker: () => AssetSearchPicker
23
+ });
24
+ module.exports = __toCommonJS(asset_search_picker_exports);
25
+ var import_jsx_runtime = require("react/jsx-runtime");
26
+ var import_react = require("react");
27
+ var import_lucide_react = require("lucide-react");
28
+ var import_cn = require("../utils/cn.js");
29
+ var import_asset_picker_cell = require("./asset-picker-cell.js");
30
+ function AssetSearchPicker({
31
+ search,
32
+ selected,
33
+ onSelect,
34
+ placeholder = "Search for an asset or creator",
35
+ className
36
+ }) {
37
+ const [query, setQuery] = (0, import_react.useState)("");
38
+ const [results, setResults] = (0, import_react.useState)([]);
39
+ const [isLoading, setIsLoading] = (0, import_react.useState)(false);
40
+ const [hasSearched, setHasSearched] = (0, import_react.useState)(false);
41
+ (0, import_react.useEffect)(() => {
42
+ const q = query.trim();
43
+ if (q.length < 2) {
44
+ setResults([]);
45
+ setHasSearched(false);
46
+ return;
47
+ }
48
+ let cancelled = false;
49
+ setIsLoading(true);
50
+ const timer = setTimeout(() => {
51
+ search(q).then((assets) => {
52
+ if (!cancelled) {
53
+ setResults(assets);
54
+ setHasSearched(true);
55
+ }
56
+ }).finally(() => {
57
+ if (!cancelled) setIsLoading(false);
58
+ });
59
+ }, 350);
60
+ return () => {
61
+ cancelled = true;
62
+ clearTimeout(timer);
63
+ };
64
+ }, [query, search]);
65
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_cn.cn)("space-y-3", className), children: [
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative", children: [
67
+ /* @__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 pointer-events-none" }),
68
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
69
+ "input",
70
+ {
71
+ type: "text",
72
+ value: query,
73
+ onChange: (e) => setQuery(e.target.value),
74
+ placeholder,
75
+ className: "w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40"
76
+ }
77
+ ),
78
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-muted-foreground" }) : null
79
+ ] }),
80
+ query.trim().length > 0 && query.trim().length < 2 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-muted-foreground text-center py-2", children: "Keep typing \u2014 2+ characters to search." }) : null,
81
+ results.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5", children: results.map((asset) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
82
+ import_asset_picker_cell.AssetPickerCell,
83
+ {
84
+ asset,
85
+ active: (0, import_asset_picker_cell.isSameAsset)(asset, selected),
86
+ onSelect
87
+ },
88
+ `${asset.contractAddress}-${asset.tokenId}`
89
+ )) }) : hasSearched && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground text-center py-4", children: [
90
+ 'No assets match "',
91
+ query.trim(),
92
+ '".'
93
+ ] }) : null
94
+ ] });
95
+ }
96
+ // Annotate the CommonJS export names for ESM import in node:
97
+ 0 && (module.exports = {
98
+ AssetSearchPicker
99
+ });
100
+ //# sourceMappingURL=asset-search-picker.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/asset-search-picker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Loader2, Search } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { OwnedAsset } from \"./asset-picker.js\";\nimport { AssetPickerCell, isSameAsset } from \"./asset-picker-cell.js\";\n\nexport interface AssetSearchPickerProps {\n /** App-supplied search — the app owns the actual fetch (e.g. GET /v1/search).\n * Called only when the query is 2+ chars, debounced by this component. */\n search: (query: string) => Promise<OwnedAsset[]>;\n selected: OwnedAsset | null;\n onSelect: (asset: OwnedAsset) => void;\n placeholder?: string;\n className?: string;\n}\n\nexport function AssetSearchPicker({\n search, selected, onSelect, placeholder = \"Search for an asset or creator\", className,\n}: AssetSearchPickerProps) {\n const [query, setQuery] = useState(\"\");\n const [results, setResults] = useState<OwnedAsset[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasSearched, setHasSearched] = useState(false);\n\n useEffect(() => {\n const q = query.trim();\n if (q.length < 2) {\n setResults([]);\n setHasSearched(false);\n return;\n }\n let cancelled = false;\n setIsLoading(true);\n const timer = setTimeout(() => {\n search(q)\n .then((assets) => {\n if (!cancelled) {\n setResults(assets);\n setHasSearched(true);\n }\n })\n .finally(() => {\n if (!cancelled) setIsLoading(false);\n });\n }, 350);\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n }, [query, search]);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n {isLoading ? (\n <Loader2 className=\"absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-muted-foreground\" />\n ) : null}\n </div>\n\n {query.trim().length > 0 && query.trim().length < 2 ? (\n <p className=\"text-xs text-muted-foreground text-center py-2\">Keep typing — 2+ characters to search.</p>\n ) : null}\n\n {results.length > 0 ? (\n <div className=\"grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5\">\n {results.map((asset) => (\n <AssetPickerCell\n key={`${asset.contractAddress}-${asset.tokenId}`}\n asset={asset}\n active={isSameAsset(asset, selected)}\n onSelect={onSelect}\n />\n ))}\n </div>\n ) : hasSearched && !isLoading ? (\n <p className=\"text-xs text-muted-foreground text-center py-4\">No assets match &quot;{query.trim()}&quot;.</p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuDM;AArDN,mBAAoC;AACpC,0BAAgC;AAChC,gBAAmB;AAEnB,+BAA6C;AAYtC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU,cAAc;AAAA,EAAkC;AAC9E,GAA2B;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAuB,CAAC,CAAC;AACvD,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,KAAK;AAEpD,8BAAU,MAAM;AACd,UAAM,IAAI,MAAM,KAAK;AACrB,QAAI,EAAE,SAAS,GAAG;AAChB,iBAAW,CAAC,CAAC;AACb,qBAAe,KAAK;AACpB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,iBAAa,IAAI;AACjB,UAAM,QAAQ,WAAW,MAAM;AAC7B,aAAO,CAAC,EACL,KAAK,CAAC,WAAW;AAChB,YAAI,CAAC,WAAW;AACd,qBAAW,MAAM;AACjB,yBAAe,IAAI;AAAA,QACrB;AAAA,MACF,CAAC,EACA,QAAQ,MAAM;AACb,YAAI,CAAC,UAAW,cAAa,KAAK;AAAA,MACpC,CAAC;AAAA,IACL,GAAG,GAAG;AACN,WAAO,MAAM;AACX,kBAAY;AACZ,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,SACE,6CAAC,SAAI,eAAW,cAAG,aAAa,SAAS,GACvC;AAAA,iDAAC,SAAI,WAAU,YACb;AAAA,kDAAC,8BAAO,WAAU,8FAA6F;AAAA,MAC/G;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC;AAAA,UACA,WAAU;AAAA;AAAA,MACZ;AAAA,MACC,YACC,4CAAC,+BAAQ,WAAU,wFAAuF,IACxG;AAAA,OACN;AAAA,IAEC,MAAM,KAAK,EAAE,SAAS,KAAK,MAAM,KAAK,EAAE,SAAS,IAChD,4CAAC,OAAE,WAAU,kDAAiD,yDAAsC,IAClG;AAAA,IAEH,QAAQ,SAAS,IAChB,4CAAC,SAAI,WAAU,2EACZ,kBAAQ,IAAI,CAAC,UACZ;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,YAAQ,sCAAY,OAAO,QAAQ;AAAA,QACnC;AAAA;AAAA,MAHK,GAAG,MAAM,eAAe,IAAI,MAAM,OAAO;AAAA,IAIhD,CACD,GACH,IACE,eAAe,CAAC,YAClB,6CAAC,OAAE,WAAU,kDAAiD;AAAA;AAAA,MAAuB,MAAM,KAAK;AAAA,MAAE;AAAA,OAAO,IACvG;AAAA,KACN;AAEJ;","names":[]}
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { OwnedAsset } from './asset-picker.cjs';
3
+
4
+ interface AssetSearchPickerProps {
5
+ /** App-supplied search — the app owns the actual fetch (e.g. GET /v1/search).
6
+ * Called only when the query is 2+ chars, debounced by this component. */
7
+ search: (query: string) => Promise<OwnedAsset[]>;
8
+ selected: OwnedAsset | null;
9
+ onSelect: (asset: OwnedAsset) => void;
10
+ placeholder?: string;
11
+ className?: string;
12
+ }
13
+ declare function AssetSearchPicker({ search, selected, onSelect, placeholder, className, }: AssetSearchPickerProps): react_jsx_runtime.JSX.Element;
14
+
15
+ export { AssetSearchPicker, type AssetSearchPickerProps };
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { OwnedAsset } from './asset-picker.js';
3
+
4
+ interface AssetSearchPickerProps {
5
+ /** App-supplied search — the app owns the actual fetch (e.g. GET /v1/search).
6
+ * Called only when the query is 2+ chars, debounced by this component. */
7
+ search: (query: string) => Promise<OwnedAsset[]>;
8
+ selected: OwnedAsset | null;
9
+ onSelect: (asset: OwnedAsset) => void;
10
+ placeholder?: string;
11
+ className?: string;
12
+ }
13
+ declare function AssetSearchPicker({ search, selected, onSelect, placeholder, className, }: AssetSearchPickerProps): react_jsx_runtime.JSX.Element;
14
+
15
+ export { AssetSearchPicker, type AssetSearchPickerProps };
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { Loader2, Search } from "lucide-react";
5
+ import { cn } from "../utils/cn.js";
6
+ import { AssetPickerCell, isSameAsset } from "./asset-picker-cell.js";
7
+ function AssetSearchPicker({
8
+ search,
9
+ selected,
10
+ onSelect,
11
+ placeholder = "Search for an asset or creator",
12
+ className
13
+ }) {
14
+ const [query, setQuery] = useState("");
15
+ const [results, setResults] = useState([]);
16
+ const [isLoading, setIsLoading] = useState(false);
17
+ const [hasSearched, setHasSearched] = useState(false);
18
+ useEffect(() => {
19
+ const q = query.trim();
20
+ if (q.length < 2) {
21
+ setResults([]);
22
+ setHasSearched(false);
23
+ return;
24
+ }
25
+ let cancelled = false;
26
+ setIsLoading(true);
27
+ const timer = setTimeout(() => {
28
+ search(q).then((assets) => {
29
+ if (!cancelled) {
30
+ setResults(assets);
31
+ setHasSearched(true);
32
+ }
33
+ }).finally(() => {
34
+ if (!cancelled) setIsLoading(false);
35
+ });
36
+ }, 350);
37
+ return () => {
38
+ cancelled = true;
39
+ clearTimeout(timer);
40
+ };
41
+ }, [query, search]);
42
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
43
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
44
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none" }),
45
+ /* @__PURE__ */ jsx(
46
+ "input",
47
+ {
48
+ type: "text",
49
+ value: query,
50
+ onChange: (e) => setQuery(e.target.value),
51
+ placeholder,
52
+ className: "w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40"
53
+ }
54
+ ),
55
+ isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-muted-foreground" }) : null
56
+ ] }),
57
+ query.trim().length > 0 && query.trim().length < 2 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center py-2", children: "Keep typing \u2014 2+ characters to search." }) : null,
58
+ results.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5", children: results.map((asset) => /* @__PURE__ */ jsx(
59
+ AssetPickerCell,
60
+ {
61
+ asset,
62
+ active: isSameAsset(asset, selected),
63
+ onSelect
64
+ },
65
+ `${asset.contractAddress}-${asset.tokenId}`
66
+ )) }) : hasSearched && !isLoading ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground text-center py-4", children: [
67
+ 'No assets match "',
68
+ query.trim(),
69
+ '".'
70
+ ] }) : null
71
+ ] });
72
+ }
73
+ export {
74
+ AssetSearchPicker
75
+ };
76
+ //# sourceMappingURL=asset-search-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/asset-search-picker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Loader2, Search } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { OwnedAsset } from \"./asset-picker.js\";\nimport { AssetPickerCell, isSameAsset } from \"./asset-picker-cell.js\";\n\nexport interface AssetSearchPickerProps {\n /** App-supplied search — the app owns the actual fetch (e.g. GET /v1/search).\n * Called only when the query is 2+ chars, debounced by this component. */\n search: (query: string) => Promise<OwnedAsset[]>;\n selected: OwnedAsset | null;\n onSelect: (asset: OwnedAsset) => void;\n placeholder?: string;\n className?: string;\n}\n\nexport function AssetSearchPicker({\n search, selected, onSelect, placeholder = \"Search for an asset or creator\", className,\n}: AssetSearchPickerProps) {\n const [query, setQuery] = useState(\"\");\n const [results, setResults] = useState<OwnedAsset[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasSearched, setHasSearched] = useState(false);\n\n useEffect(() => {\n const q = query.trim();\n if (q.length < 2) {\n setResults([]);\n setHasSearched(false);\n return;\n }\n let cancelled = false;\n setIsLoading(true);\n const timer = setTimeout(() => {\n search(q)\n .then((assets) => {\n if (!cancelled) {\n setResults(assets);\n setHasSearched(true);\n }\n })\n .finally(() => {\n if (!cancelled) setIsLoading(false);\n });\n }, 350);\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n }, [query, search]);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-4 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n {isLoading ? (\n <Loader2 className=\"absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-muted-foreground\" />\n ) : null}\n </div>\n\n {query.trim().length > 0 && query.trim().length < 2 ? (\n <p className=\"text-xs text-muted-foreground text-center py-2\">Keep typing — 2+ characters to search.</p>\n ) : null}\n\n {results.length > 0 ? (\n <div className=\"grid grid-cols-3 sm:grid-cols-4 gap-2.5 max-h-80 overflow-y-auto pr-0.5\">\n {results.map((asset) => (\n <AssetPickerCell\n key={`${asset.contractAddress}-${asset.tokenId}`}\n asset={asset}\n active={isSameAsset(asset, selected)}\n onSelect={onSelect}\n />\n ))}\n </div>\n ) : hasSearched && !isLoading ? (\n <p className=\"text-xs text-muted-foreground text-center py-4\">No assets match &quot;{query.trim()}&quot;.</p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";AAuDM,SACE,KADF;AArDN,SAAS,WAAW,gBAAgB;AACpC,SAAS,SAAS,cAAc;AAChC,SAAS,UAAU;AAEnB,SAAS,iBAAiB,mBAAmB;AAYtC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU,cAAc;AAAA,EAAkC;AAC9E,GAA2B;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAuB,CAAC,CAAC;AACvD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AAEpD,YAAU,MAAM;AACd,UAAM,IAAI,MAAM,KAAK;AACrB,QAAI,EAAE,SAAS,GAAG;AAChB,iBAAW,CAAC,CAAC;AACb,qBAAe,KAAK;AACpB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,iBAAa,IAAI;AACjB,UAAM,QAAQ,WAAW,MAAM;AAC7B,aAAO,CAAC,EACL,KAAK,CAAC,WAAW;AAChB,YAAI,CAAC,WAAW;AACd,qBAAW,MAAM;AACjB,yBAAe,IAAI;AAAA,QACrB;AAAA,MACF,CAAC,EACA,QAAQ,MAAM;AACb,YAAI,CAAC,UAAW,cAAa,KAAK;AAAA,MACpC,CAAC;AAAA,IACL,GAAG,GAAG;AACN,WAAO,MAAM;AACX,kBAAY;AACZ,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,SACE,qBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GACvC;AAAA,yBAAC,SAAI,WAAU,YACb;AAAA,0BAAC,UAAO,WAAU,8FAA6F;AAAA,MAC/G;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC;AAAA,UACA,WAAU;AAAA;AAAA,MACZ;AAAA,MACC,YACC,oBAAC,WAAQ,WAAU,wFAAuF,IACxG;AAAA,OACN;AAAA,IAEC,MAAM,KAAK,EAAE,SAAS,KAAK,MAAM,KAAK,EAAE,SAAS,IAChD,oBAAC,OAAE,WAAU,kDAAiD,yDAAsC,IAClG;AAAA,IAEH,QAAQ,SAAS,IAChB,oBAAC,SAAI,WAAU,2EACZ,kBAAQ,IAAI,CAAC,UACZ;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,QAAQ,YAAY,OAAO,QAAQ;AAAA,QACnC;AAAA;AAAA,MAHK,GAAG,MAAM,eAAe,IAAI,MAAM,OAAO;AAAA,IAIhD,CACD,GACH,IACE,eAAe,CAAC,YAClB,qBAAC,OAAE,WAAU,kDAAiD;AAAA;AAAA,MAAuB,MAAM,KAAK;AAAA,MAAE;AAAA,OAAO,IACvG;AAAA,KACN;AAEJ;","names":[]}