@medialane/ui 0.320.0 → 0.321.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.
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
7
+ variant?: "default" | "secondary" | "outline" | "destructive" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
10
10
  }
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
7
+ variant?: "default" | "secondary" | "outline" | "destructive" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
10
10
  }
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | "premium" | "tonal" | "gradient-border" | "gradient-fill" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "outline" | "destructive" | "ghost" | "premium" | "tonal" | "gradient-border" | "gradient-fill" | null | undefined;
7
7
  size?: "default" | "icon" | "sm" | "lg" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | "premium" | "tonal" | "gradient-border" | "gradient-fill" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "outline" | "destructive" | "ghost" | "premium" | "tonal" | "gradient-border" | "gradient-fill" | null | undefined;
7
7
  size?: "default" | "icon" | "sm" | "lg" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -118,14 +118,15 @@ function CollectionFiltersTrigger({
118
118
  "button",
119
119
  {
120
120
  onClick: () => onRemoveFilter(traitType, value),
121
- className: "inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors",
121
+ title: `${traitType}: ${value}`,
122
+ className: "inline-flex max-w-full min-w-0 items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors",
122
123
  children: [
123
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-muted-foreground text-2xs", children: [
124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-muted-foreground text-2xs shrink-0", children: [
124
125
  traitType,
125
126
  ":"
126
127
  ] }),
127
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: value }),
128
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "h-3 w-3 text-muted-foreground ml-0.5" })
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate", children: value }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "h-3 w-3 text-muted-foreground ml-0.5 shrink-0" })
129
130
  ]
130
131
  },
131
132
  `${traitType}:${value}`
@@ -141,7 +142,7 @@ function CollectionFiltersTrigger({
141
142
  ] })
142
143
  ] });
143
144
  }
144
- const PILL_BASE = "inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors";
145
+ const PILL_BASE = "inline-flex max-w-full min-w-0 items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors";
145
146
  const PILL_ON = "border-primary bg-primary text-primary-foreground";
146
147
  const PILL_OFF = "border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30";
147
148
  function CollectionFiltersBody({
@@ -151,7 +152,7 @@ function CollectionFiltersBody({
151
152
  selected,
152
153
  onToggleValue
153
154
  }) {
154
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 overflow-y-auto px-5 py-3 space-y-4", children: [
155
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 min-w-0 overflow-y-auto overflow-x-hidden px-5 py-3 space-y-4", children: [
155
156
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
156
157
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-medium", children: "Sort by" }) }),
157
158
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-1.5", children: SORT_OPTIONS.map(({ value, label }) => {
@@ -176,9 +177,9 @@ function CollectionFiltersBody({
176
177
  const activeValues = selected[section.traitType] ?? [];
177
178
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
178
179
  i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px w-full bg-border mb-4" }),
179
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
180
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-medium", children: section.traitType }),
181
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-2xs text-muted-foreground", children: [
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-3 mb-2", children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "min-w-0 truncate text-sm font-medium", title: section.traitType, children: section.traitType }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "shrink-0 text-2xs text-muted-foreground", children: [
182
183
  section.values.length,
183
184
  " values"
184
185
  ] })
@@ -189,11 +190,12 @@ function CollectionFiltersBody({
189
190
  "button",
190
191
  {
191
192
  onClick: () => onToggleValue(section.traitType, value),
193
+ title: value,
192
194
  className: (0, import_cn.cn)(PILL_BASE, isSelected ? PILL_ON : PILL_OFF),
193
195
  children: [
194
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Check, { className: "h-3 w-3" }),
195
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: value }),
196
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: isSelected ? "opacity-80" : "opacity-60", children: count })
196
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Check, { className: "h-3 w-3 shrink-0" }),
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate", children: value }),
198
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: (0, import_cn.cn)("shrink-0", isSelected ? "opacity-80" : "opacity-60"), children: count })
197
199
  ]
198
200
  },
199
201
  value
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n showButton?: boolean;\n showChips?: boolean;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n showButton = true,\n showChips = true,\n}: CollectionFiltersTriggerProps) {\n if (!showButton && (!showChips || (sortIsDefault && activeEntries.length === 0))) return null;\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n {showButton && (\n <button onClick={onOpen} className={TRIGGER_BTN} aria-label=\"Filters\">\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n <span className=\"hidden sm:inline\">Filters</span>\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n )}\n {showChips && (\n <>\n\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">{traitType}:</span>\n <span>{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 overflow-y-auto px-5 py-3 space-y-4\">\n\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium\">{section.traitType}</span>\n <span className=\"text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{value}</span>\n <span className={isSelected ? \"opacity-80\" : \"opacity-60\"}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsHQ;AApHR,mBAAwB;AACxB,0BAA4C;AAC5C,gBAAmB;AAGnB,MAAM,uBAAuB,oBAAI,IAAI,CAAC,cAAc,CAAC;AAE9C,MAAM,eAAiE;AAAA,EAC5E,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,EACnC,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,EACnC,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,EAC/B,EAAE,OAAO,SAAS,OAAO,QAAQ;AACnC;AAOO,SAAS,qBACd,QACA,UACA,UACA,MACA,cACA;AAEA,QAAM,oBAAgC,sBAAQ,MAAM;AAClD,UAAM,MAAM,oBAAI,IAAiC;AACjD,eAAW,SAAS,QAAQ;AAC1B,YAAM,QAAQ,MAAM,QAAQ,MAAM,UAAU,UAAU,IACjD,MAAM,SAAS,aAChB,CAAC;AACL,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,KAAK,cAAc,KAAK,SAAS,KAAM;AAC5C,YAAI,qBAAqB,IAAI,KAAK,WAAW,YAAY,CAAC,EAAG;AAC7D,YAAI,CAAC,IAAI,IAAI,KAAK,UAAU,EAAG,KAAI,IAAI,KAAK,YAAY,oBAAI,IAAI,CAAC;AACjE,cAAM,SAAS,IAAI,IAAI,KAAK,UAAU;AACtC,cAAM,IAAI,OAAO,KAAK,KAAK;AAC3B,eAAO,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,CAAC;AAAA,MACxC;AAAA,IACF;AACA,WAAO,MAAM,KAAK,IAAI,QAAQ,CAAC,EAC5B,IAAI,CAAC,CAAC,WAAW,MAAM,OAAO;AAAA,MAC7B;AAAA,MACA,QAAQ,MAAM,KAAK,OAAO,QAAQ,CAAC,EAChC,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,EAAE,OAAO,MAAM,EAAE,EAC1C,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AAAA,IACvE,EAAE,EACD,OAAO,CAAC,YAAY,QAAQ,OAAO,UAAU,CAAC;AAAA,EACnD,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,gBAAgB,OAAO,QAAQ,QAAQ,EAAE;AAAA,IAAQ,CAAC,CAAC,WAAW,MAAM,MACxE,OAAO,IAAI,CAAC,WAAW,EAAE,WAAW,MAAM,EAAE;AAAA,EAC9C;AACA,QAAM,mBAAmB,cAAc;AACvC,QAAM,gBAAgB,SAAS;AAC/B,QAAM,mBAAmB,oBAAoB,gBAAgB,IAAI;AAEjE,WAAS,YAAY,WAAmB,OAAe;AACrD,UAAM,UAAU,SAAS,SAAS,KAAK,CAAC;AACxC,UAAM,OAAO,QAAQ,SAAS,KAAK,IAC/B,QAAQ,OAAO,CAAC,MAAM,MAAM,KAAK,IACjC,CAAC,GAAG,SAAS,KAAK;AACtB,UAAM,eAAe,EAAE,GAAG,SAAS;AACnC,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO,aAAa,SAAS;AAAA,IAC/B,OAAO;AACL,mBAAa,SAAS,IAAI;AAAA,IAC5B;AACA,aAAS,YAAY;AAAA,EACvB;AAEA,WAAS,WAAW;AAClB,aAAS,CAAC,CAAC;AACX,iBAAa,QAAQ;AAAA,EACvB;AAEA,WAAS,aAAa,WAAmB,OAAe;AACtD,gBAAY,WAAW,KAAK;AAAA,EAC9B;AAEA,SAAO,EAAE,eAAe,eAAe,kBAAkB,eAAe,aAAa,UAAU,aAAa;AAC9G;AAEA,MAAM,cAAc;AAeb,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,YAAY;AACd,GAAkC;AAChC,MAAI,CAAC,eAAe,CAAC,aAAc,iBAAiB,cAAc,WAAW,GAAK,QAAO;AACzF,SACE,6CAAC,SAAI,WAAU,iDACZ;AAAA,kBACC,6CAAC,YAAO,SAAS,QAAQ,WAAW,aAAa,cAAW,WAC1D;AAAA,kDAAC,yCAAkB,WAAU,eAAc;AAAA,MAC3C,4CAAC,UAAK,WAAU,oBAAmB,qBAAO;AAAA,MACzC,mBAAmB,KAClB,4CAAC,UAAK,WAAU,8HACb,4BACH;AAAA,OAEJ;AAAA,IAED,aACC,4EAED;AAAA,OAAC,iBACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UAEV;AAAA,wDAAC,UAAK,WAAU,kCAAiC,mBAAK;AAAA,YACtD,4CAAC,UAAM,uBAAa,KAAK,CAAC,MAAM,EAAE,UAAU,IAAI,GAAG,OAAM;AAAA,YACzD,4CAAC,yBAAE,WAAU,wCAAuC;AAAA;AAAA;AAAA,MACtD;AAAA,MAGD,cAAc,IAAI,CAAC,EAAE,WAAW,MAAM,MACrC;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,MAAM,eAAe,WAAW,KAAK;AAAA,UAC9C,WAAU;AAAA,UAEV;AAAA,yDAAC,UAAK,WAAU,kCAAkC;AAAA;AAAA,cAAU;AAAA,eAAC;AAAA,YAC7D,4CAAC,UAAM,iBAAM;AAAA,YACb,4CAAC,yBAAE,WAAU,wCAAuC;AAAA;AAAA;AAAA,QAN/C,GAAG,SAAS,IAAI,KAAK;AAAA,MAO5B,CACD;AAAA,MAEA,mBAAmB,KAClB;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,OAEA;AAAA,KAEJ;AAEJ;AAUA,MAAM,YAAY;AAClB,MAAM,UAAU;AAChB,MAAM,WAAW;AAEV,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA+B;AAC7B,SACE,6CAAC,SAAI,WAAU,8CAEb;AAAA,iDAAC,SACC;AAAA,kDAAC,SAAI,WAAU,QACb,sDAAC,UAAK,WAAU,uBAAsB,qBAAO,GAC/C;AAAA,MACA,4CAAC,SAAI,WAAU,0BACZ,uBAAa,IAAI,CAAC,EAAE,OAAO,MAAM,MAAM;AACtC,cAAM,aAAa,SAAS;AAC5B,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,SAAS,MAAM,aAAa,KAAK;AAAA,YACjC,eAAW,cAAG,WAAW,aAAa,UAAU,QAAQ;AAAA,YAEvD;AAAA,4BAAc,4CAAC,6BAAM,WAAU,WAAU;AAAA,cAC1C,4CAAC,UAAM,iBAAM;AAAA;AAAA;AAAA,UALR;AAAA,QAMP;AAAA,MAEJ,CAAC,GACH;AAAA,OACF;AAAA,IAEC,cAAc,SAAS,KACtB,4EACE;AAAA,kDAAC,SAAI,WAAU,yBAAwB;AAAA,MACtC,cAAc,IAAI,CAAC,SAAS,MAAM;AACjC,cAAM,eAAe,SAAS,QAAQ,SAAS,KAAK,CAAC;AACrD,eACE,6CAAC,SACE;AAAA,cAAI,KAAK,4CAAC,SAAI,WAAU,8BAA6B;AAAA,UACtD,6CAAC,SAAI,WAAU,0CACb;AAAA,wDAAC,UAAK,WAAU,uBAAuB,kBAAQ,WAAU;AAAA,YACzD,6CAAC,UAAK,WAAU,kCACb;AAAA,sBAAQ,OAAO;AAAA,cAAO;AAAA,eACzB;AAAA,aACF;AAAA,UACA,4CAAC,SAAI,WAAU,0BACZ,kBAAQ,OAAO,IAAI,CAAC,EAAE,OAAO,MAAM,MAAM;AACxC,kBAAM,aAAa,aAAa,SAAS,KAAK;AAC9C,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,SAAS,MAAM,cAAc,QAAQ,WAAW,KAAK;AAAA,gBACrD,eAAW,cAAG,WAAW,aAAa,UAAU,QAAQ;AAAA,gBAEvD;AAAA,gCAAc,4CAAC,6BAAM,WAAU,WAAU;AAAA,kBAC1C,4CAAC,UAAM,iBAAM;AAAA,kBACb,4CAAC,UAAK,WAAW,aAAa,eAAe,cAC1C,iBACH;AAAA;AAAA;AAAA,cARK;AAAA,YASP;AAAA,UAEJ,CAAC,GACH;AAAA,aAzBQ,QAAQ,SA0BlB;AAAA,MAEJ,CAAC;AAAA,OACH;AAAA,KAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n showButton?: boolean;\n showChips?: boolean;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n showButton = true,\n showChips = true,\n}: CollectionFiltersTriggerProps) {\n if (!showButton && (!showChips || (sortIsDefault && activeEntries.length === 0))) return null;\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n {showButton && (\n <button onClick={onOpen} className={TRIGGER_BTN} aria-label=\"Filters\">\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n <span className=\"hidden sm:inline\">Filters</span>\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n )}\n {showChips && (\n <>\n\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n title={`${traitType}: ${value}`}\n className=\"inline-flex max-w-full min-w-0 items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs shrink-0\">{traitType}:</span>\n <span className=\"truncate\">{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5 shrink-0\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex max-w-full min-w-0 items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 min-w-0 overflow-y-auto overflow-x-hidden px-5 py-3 space-y-4\">\n\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between gap-3 mb-2\">\n <span className=\"min-w-0 truncate text-sm font-medium\" title={section.traitType}>{section.traitType}</span>\n <span className=\"shrink-0 text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n title={value}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3 shrink-0\" />}\n <span className=\"truncate\">{value}</span>\n <span className={cn(\"shrink-0\", isSelected ? \"opacity-80\" : \"opacity-60\")}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -92,14 +92,15 @@ function CollectionFiltersTrigger({
92
92
  "button",
93
93
  {
94
94
  onClick: () => onRemoveFilter(traitType, value),
95
- className: "inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors",
95
+ title: `${traitType}: ${value}`,
96
+ className: "inline-flex max-w-full min-w-0 items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors",
96
97
  children: [
97
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-2xs", children: [
98
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-2xs shrink-0", children: [
98
99
  traitType,
99
100
  ":"
100
101
  ] }),
101
- /* @__PURE__ */ jsx("span", { children: value }),
102
- /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-muted-foreground ml-0.5" })
102
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: value }),
103
+ /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-muted-foreground ml-0.5 shrink-0" })
103
104
  ]
104
105
  },
105
106
  `${traitType}:${value}`
@@ -115,7 +116,7 @@ function CollectionFiltersTrigger({
115
116
  ] })
116
117
  ] });
117
118
  }
118
- const PILL_BASE = "inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors";
119
+ const PILL_BASE = "inline-flex max-w-full min-w-0 items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors";
119
120
  const PILL_ON = "border-primary bg-primary text-primary-foreground";
120
121
  const PILL_OFF = "border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30";
121
122
  function CollectionFiltersBody({
@@ -125,7 +126,7 @@ function CollectionFiltersBody({
125
126
  selected,
126
127
  onToggleValue
127
128
  }) {
128
- return /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-3 space-y-4", children: [
129
+ return /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 overflow-y-auto overflow-x-hidden px-5 py-3 space-y-4", children: [
129
130
  /* @__PURE__ */ jsxs("div", { children: [
130
131
  /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Sort by" }) }),
131
132
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: SORT_OPTIONS.map(({ value, label }) => {
@@ -150,9 +151,9 @@ function CollectionFiltersBody({
150
151
  const activeValues = selected[section.traitType] ?? [];
151
152
  return /* @__PURE__ */ jsxs("div", { children: [
152
153
  i > 0 && /* @__PURE__ */ jsx("div", { className: "h-px w-full bg-border mb-4" }),
153
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
154
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: section.traitType }),
155
- /* @__PURE__ */ jsxs("span", { className: "text-2xs text-muted-foreground", children: [
154
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 mb-2", children: [
155
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-sm font-medium", title: section.traitType, children: section.traitType }),
156
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-2xs text-muted-foreground", children: [
156
157
  section.values.length,
157
158
  " values"
158
159
  ] })
@@ -163,11 +164,12 @@ function CollectionFiltersBody({
163
164
  "button",
164
165
  {
165
166
  onClick: () => onToggleValue(section.traitType, value),
167
+ title: value,
166
168
  className: cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF),
167
169
  children: [
168
- isSelected && /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
169
- /* @__PURE__ */ jsx("span", { children: value }),
170
- /* @__PURE__ */ jsx("span", { className: isSelected ? "opacity-80" : "opacity-60", children: count })
170
+ isSelected && /* @__PURE__ */ jsx(Check, { className: "h-3 w-3 shrink-0" }),
171
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: value }),
172
+ /* @__PURE__ */ jsx("span", { className: cn("shrink-0", isSelected ? "opacity-80" : "opacity-60"), children: count })
171
173
  ]
172
174
  },
173
175
  value
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n showButton?: boolean;\n showChips?: boolean;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n showButton = true,\n showChips = true,\n}: CollectionFiltersTriggerProps) {\n if (!showButton && (!showChips || (sortIsDefault && activeEntries.length === 0))) return null;\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n {showButton && (\n <button onClick={onOpen} className={TRIGGER_BTN} aria-label=\"Filters\">\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n <span className=\"hidden sm:inline\">Filters</span>\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n )}\n {showChips && (\n <>\n\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">{traitType}:</span>\n <span>{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 overflow-y-auto px-5 py-3 space-y-4\">\n\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium\">{section.traitType}</span>\n <span className=\"text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{value}</span>\n <span className={isSelected ? \"opacity-80\" : \"opacity-60\"}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n showButton?: boolean;\n showChips?: boolean;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n showButton = true,\n showChips = true,\n}: CollectionFiltersTriggerProps) {\n if (!showButton && (!showChips || (sortIsDefault && activeEntries.length === 0))) return null;\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n {showButton && (\n <button onClick={onOpen} className={TRIGGER_BTN} aria-label=\"Filters\">\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n <span className=\"hidden sm:inline\">Filters</span>\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n )}\n {showChips && (\n <>\n\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n title={`${traitType}: ${value}`}\n className=\"inline-flex max-w-full min-w-0 items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs shrink-0\">{traitType}:</span>\n <span className=\"truncate\">{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5 shrink-0\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex max-w-full min-w-0 items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 min-w-0 overflow-y-auto overflow-x-hidden px-5 py-3 space-y-4\">\n\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between gap-3 mb-2\">\n <span className=\"min-w-0 truncate text-sm font-medium\" title={section.traitType}>{section.traitType}</span>\n <span className=\"shrink-0 text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n title={value}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3 shrink-0\" />}\n <span className=\"truncate\">{value}</span>\n <span className={cn(\"shrink-0\", isSelected ? \"opacity-80\" : \"opacity-60\")}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -16,6 +16,7 @@ declare const editionMintSchema: z.ZodObject<{
16
16
  royalty: z.ZodNumber;
17
17
  }, "strip", z.ZodTypeAny, {
18
18
  name: string;
19
+ value: string;
19
20
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
20
21
  licenseType: string;
21
22
  commercialUse: "Yes" | "No";
@@ -24,12 +25,12 @@ declare const editionMintSchema: z.ZodObject<{
24
25
  geographicScope: string;
25
26
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
26
27
  royalty: number;
27
- value: string;
28
28
  recipient: string;
29
29
  description?: string | undefined;
30
30
  external_url?: string | undefined;
31
31
  }, {
32
32
  name: string;
33
+ value: string;
33
34
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
34
35
  licenseType: string;
35
36
  commercialUse: "Yes" | "No";
@@ -38,7 +39,6 @@ declare const editionMintSchema: z.ZodObject<{
38
39
  geographicScope: string;
39
40
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
40
41
  royalty: number;
41
- value: string;
42
42
  recipient: string;
43
43
  description?: string | undefined;
44
44
  external_url?: string | undefined;
@@ -16,6 +16,7 @@ declare const editionMintSchema: z.ZodObject<{
16
16
  royalty: z.ZodNumber;
17
17
  }, "strip", z.ZodTypeAny, {
18
18
  name: string;
19
+ value: string;
19
20
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
20
21
  licenseType: string;
21
22
  commercialUse: "Yes" | "No";
@@ -24,12 +25,12 @@ declare const editionMintSchema: z.ZodObject<{
24
25
  geographicScope: string;
25
26
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
26
27
  royalty: number;
27
- value: string;
28
28
  recipient: string;
29
29
  description?: string | undefined;
30
30
  external_url?: string | undefined;
31
31
  }, {
32
32
  name: string;
33
+ value: string;
33
34
  ipType: "Audio" | "Video" | "Art" | "Photography" | "NFT" | "Software" | "RWA" | "Patents" | "Posts" | "Publications" | "Documents" | "Custom";
34
35
  licenseType: string;
35
36
  commercialUse: "Yes" | "No";
@@ -38,7 +39,6 @@ declare const editionMintSchema: z.ZodObject<{
38
39
  geographicScope: string;
39
40
  aiPolicy: "Allowed" | "Not Allowed" | "Training Only";
40
41
  royalty: number;
41
- value: string;
42
42
  recipient: string;
43
43
  description?: string | undefined;
44
44
  external_url?: string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medialane/ui",
3
- "version": "0.320.0",
3
+ "version": "0.321.0",
4
4
  "description": "Shared UI components for Medialane apps",
5
5
  "type": "module",
6
6
  "sideEffects": false,