@medialane/ui 0.5.3 → 0.6.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.
@@ -48,7 +48,7 @@ function useNavCommandMenu() {
48
48
  close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE))
49
49
  };
50
50
  }
51
- function NavCommandMenu({ commands, trigger, accountSlot }) {
51
+ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
52
52
  const [open, setOpen] = React.useState(false);
53
53
  const router = (0, import_navigation.useRouter)();
54
54
  const inputRef = React.useRef(null);
@@ -153,14 +153,14 @@ function NavCommandMenu({ commands, trigger, accountSlot }) {
153
153
  className: "w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl",
154
154
  onClick: (e) => e.stopPropagation(),
155
155
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_cmdk.Command, { shouldFilter: true, label: "Medialane navigation", children: [
156
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-border/40", children: [
157
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Search, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3 px-4 py-3.5 border-b border-border/40", children: [
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Search, { className: "h-[18px] w-[18px] text-muted-foreground shrink-0" }),
158
158
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
159
159
  import_cmdk.Command.Input,
160
160
  {
161
161
  ref: inputRef,
162
162
  placeholder: "Type a command or search\u2026",
163
- className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
163
+ className: "flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground"
164
164
  }
165
165
  ),
166
166
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -175,45 +175,52 @@ function NavCommandMenu({ commands, trigger, accountSlot }) {
175
175
  ] }),
176
176
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_cmdk.Command.List, { className: "max-h-[60vh] overflow-y-auto p-2", children: [
177
177
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_cmdk.Command.Empty, { className: "py-8 text-center text-sm text-muted-foreground", children: "No results found." }),
178
- commands.map((group, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(React.Fragment, { children: [
179
- i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_cmdk.Command.Separator, { className: "my-1 h-px bg-border/40" }),
180
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
181
- import_cmdk.Command.Group,
182
- {
183
- heading: group.heading,
184
- className: (0, import_cn.cn)(
185
- "[&_[cmdk-group-heading]]:px-2",
186
- "[&_[cmdk-group-heading]]:py-1.5",
187
- "[&_[cmdk-group-heading]]:text-xs",
188
- "[&_[cmdk-group-heading]]:font-medium",
189
- "[&_[cmdk-group-heading]]:text-muted-foreground"
190
- ),
191
- children: group.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
192
- import_cmdk.Command.Item,
193
- {
194
- value: [item.label, ...item.keywords ?? []].join(" "),
195
- onSelect: () => runCommand(item),
196
- className: (0, import_cn.cn)(
197
- "flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm cursor-pointer",
198
- "transition-colors",
199
- "aria-selected:bg-muted/60"
200
- ),
201
- children: [
202
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(item.icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
203
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex-1", children: item.label }),
204
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { className: "h-3.5 w-3.5 text-muted-foreground/40 shrink-0" })
205
- ]
206
- },
207
- item.id
208
- ))
209
- }
210
- )
211
- ] }, group.heading))
178
+ commands.map((group, i) => {
179
+ const primary = !group.heading;
180
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(React.Fragment, { children: [
181
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_cmdk.Command.Separator, { className: "my-1.5 h-px bg-border/40" }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
183
+ import_cmdk.Command.Group,
184
+ {
185
+ heading: group.heading,
186
+ className: (0, import_cn.cn)(
187
+ "[&_[cmdk-group-heading]]:px-2",
188
+ "[&_[cmdk-group-heading]]:pt-1.5",
189
+ "[&_[cmdk-group-heading]]:pb-1",
190
+ "[&_[cmdk-group-heading]]:text-[11px]",
191
+ "[&_[cmdk-group-heading]]:font-semibold",
192
+ "[&_[cmdk-group-heading]]:uppercase",
193
+ "[&_[cmdk-group-heading]]:tracking-wider",
194
+ "[&_[cmdk-group-heading]]:text-muted-foreground/70"
195
+ ),
196
+ children: group.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
197
+ import_cmdk.Command.Item,
198
+ {
199
+ value: [item.label, ...item.keywords ?? []].join(" "),
200
+ onSelect: () => runCommand(item),
201
+ className: (0, import_cn.cn)(
202
+ "group/item flex items-center gap-3 rounded-xl cursor-pointer",
203
+ "transition-colors duration-150",
204
+ "aria-selected:bg-primary/10",
205
+ primary ? "px-2.5 py-2.5 text-[15px] font-medium" : "px-3 py-2 text-sm"
206
+ ),
207
+ children: [
208
+ primary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-muted/50 group-aria-selected/item:bg-primary/15 transition-colors shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(item.icon, { className: "h-[18px] w-[18px] text-foreground/80 group-aria-selected/item:text-primary transition-colors" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(item.icon, { className: "h-4 w-4 text-muted-foreground group-aria-selected/item:text-foreground transition-colors shrink-0" }),
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex-1 truncate", children: item.label }),
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { className: "h-3.5 w-3.5 text-muted-foreground/30 group-aria-selected/item:text-primary/70 transition-colors shrink-0" })
211
+ ]
212
+ },
213
+ item.id
214
+ ))
215
+ }
216
+ )
217
+ ] }, group.heading ?? `__primary-${i}`);
218
+ })
212
219
  ] }),
213
220
  accountSlot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border-t border-border/40 bg-background/40 px-3 py-3", children: accountSlot }),
214
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-4 py-2.5 border-t border-border/40 flex items-center justify-between", children: [
215
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[10px] text-muted-foreground/50", children: "medialane" }),
216
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("kbd", { className: "text-[10px] text-muted-foreground/50 font-mono", children: "\u2318K" })
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-3 py-2 border-t border-border/40 flex items-center justify-between gap-3", children: [
222
+ footerSlot ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[10px] text-muted-foreground/50 pl-1", children: "medialane" }),
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("kbd", { className: "text-[10px] text-muted-foreground/50 font-mono shrink-0", children: "\u2318K" })
217
224
  ] })
218
225
  ] })
219
226
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n}\n\nexport interface NavCommandGroup {\n heading: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Aurora blobs — vivid intensity */}\n <motion.div\n className=\"nav-canvas-aurora\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"aurora-purple animate-blob w-[60vw] h-[60vw] -top-[10vw] -left-[10vw]\"\n style={{ opacity: 0.25 }} />\n <div className=\"aurora-blue animate-blob-slow w-[50vw] h-[50vw] -top-[5vw] -right-[10vw]\"\n style={{ opacity: 0.2 }} />\n <div className=\"aurora-rose animate-blob w-[45vw] h-[45vw] bottom-[5vw] -left-[5vw]\"\n style={{ opacity: 0.15 }} />\n <div className=\"aurora-orange animate-blob-slow w-[40vw] h-[40vw] -bottom-[5vw] -right-[5vw]\"\n style={{ opacity: 0.15 }} />\n </motion.div>\n\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-center justify-center p-4\"\n initial={{ opacity: 0, scale: 0.97 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.97 }}\n transition={{ duration: 0.15, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className=\"w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\">\n {/* Search bar */}\n <div className=\"flex items-center gap-3 px-4 py-3 border-b border-border/40\">\n <Search className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <Command.Input\n ref={inputRef}\n placeholder=\"Type a command or search…\"\n className=\"flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground\"\n />\n <button\n onClick={() => setOpen(false)}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"max-h-[60vh] overflow-y-auto p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => (\n <React.Fragment key={group.heading}>\n {i > 0 && (\n <Command.Separator className=\"my-1 h-px bg-border/40\" />\n )}\n <Command.Group\n heading={group.heading}\n className={cn(\n \"[&_[cmdk-group-heading]]:px-2\",\n \"[&_[cmdk-group-heading]]:py-1.5\",\n \"[&_[cmdk-group-heading]]:text-xs\",\n \"[&_[cmdk-group-heading]]:font-medium\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground\"\n )}\n >\n {group.items.map((item) => (\n <Command.Item\n key={item.id}\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={() => runCommand(item)}\n className={cn(\n \"flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm cursor-pointer\",\n \"transition-colors\",\n \"aria-selected:bg-muted/60\"\n )}\n >\n <item.icon className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <span className=\"flex-1\">{item.label}</span>\n <ArrowRight className=\"h-3.5 w-3.5 text-muted-foreground/40 shrink-0\" />\n </Command.Item>\n ))}\n </Command.Group>\n </React.Fragment>\n ))}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-background/40 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"px-4 py-2.5 border-t border-border/40 flex items-center justify-between\">\n <span className=\"text-[10px] text-muted-foreground/50\">medialane</span>\n <kbd className=\"text-[10px] text-muted-foreground/50 font-mono\">⌘K</kbd>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Aurora blobs — vivid intensity */}\n <motion.div\n className=\"nav-canvas-aurora\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"aurora-purple animate-blob w-[60vw] h-[60vw] -top-[10vw] -left-[10vw]\"\n style={{ opacity: 0.25 }} />\n <div className=\"aurora-blue animate-blob-slow w-[50vw] h-[50vw] -top-[5vw] -right-[10vw]\"\n style={{ opacity: 0.2 }} />\n <div className=\"aurora-rose animate-blob w-[45vw] h-[45vw] bottom-[5vw] -left-[5vw]\"\n style={{ opacity: 0.15 }} />\n <div className=\"aurora-orange animate-blob-slow w-[40vw] h-[40vw] -bottom-[5vw] -right-[5vw]\"\n style={{ opacity: 0.15 }} />\n </motion.div>\n\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-center justify-center p-4\"\n initial={{ opacity: 0, scale: 0.97 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.97 }}\n transition={{ duration: 0.15, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className=\"w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\">\n {/* Search bar */}\n <div className=\"flex items-center gap-3 px-4 py-3.5 border-b border-border/40\">\n <Search className=\"h-[18px] w-[18px] text-muted-foreground shrink-0\" />\n <Command.Input\n ref={inputRef}\n placeholder=\"Type a command or search…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <button\n onClick={() => setOpen(false)}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"max-h-[60vh] overflow-y-auto p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group\n heading={group.heading}\n className={cn(\n \"[&_[cmdk-group-heading]]:px-2\",\n \"[&_[cmdk-group-heading]]:pt-1.5\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[11px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-wider\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/70\"\n )}\n >\n {group.items.map((item) => (\n <Command.Item\n key={item.id}\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={() => runCommand(item)}\n className={cn(\n \"group/item flex items-center gap-3 rounded-xl cursor-pointer\",\n \"transition-colors duration-150\",\n \"aria-selected:bg-primary/10\",\n primary\n ? \"px-2.5 py-2.5 text-[15px] font-medium\"\n : \"px-3 py-2 text-sm\"\n )}\n >\n {primary ? (\n <span className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-muted/50 group-aria-selected/item:bg-primary/15 transition-colors shrink-0\">\n <item.icon className=\"h-[18px] w-[18px] text-foreground/80 group-aria-selected/item:text-primary transition-colors\" />\n </span>\n ) : (\n <item.icon className=\"h-4 w-4 text-muted-foreground group-aria-selected/item:text-foreground transition-colors shrink-0\" />\n )}\n <span className=\"flex-1 truncate\">{item.label}</span>\n <ArrowRight className=\"h-3.5 w-3.5 text-muted-foreground/30 group-aria-selected/item:text-primary/70 transition-colors shrink-0\" />\n </Command.Item>\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-background/40 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"px-3 py-2 border-t border-border/40 flex items-center justify-between gap-3\">\n {footerSlot ?? (\n <span className=\"text-[10px] text-muted-foreground/50 pl-1\">medialane</span>\n )}\n <kbd className=\"text-[10px] text-muted-foreground/50 font-mono shrink-0\">⌘K</kbd>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkHU;AAhHV,YAAuB;AACvB,kBAAwB;AACxB,wBAA0B;AAC1B,0BAAsC;AACtC,2BAAwC;AACxC,gBAAmB;AAgDnB,MAAM,cAAe;AACrB,MAAM,eAAe;AAEd,SAAS,oBAAoB;AAClC,SAAO;AAAA,IACL,MAAO,MAAM,SAAS,cAAc,IAAI,YAAY,WAAW,CAAC;AAAA,IAChE,OAAO,MAAM,SAAS,cAAc,IAAI,YAAY,YAAY,CAAC;AAAA,EACnE;AACF;AAIO,SAAS,eAAe,EAAE,UAAU,SAAS,aAAa,WAAW,GAAwB;AAClG,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,aAAS,6BAAU;AACzB,QAAM,WAAW,MAAM,OAAyB,IAAI;AAEpD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAM;AACX,UAAM,IAAI,WAAW,MAAM,SAAS,SAAS,MAAM,GAAG,EAAE;AACxD,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,UAAU,MAAM;AACpB,UAAM,QAAQ,CAAC,MAAqB;AAClC,UAAI,EAAE,QAAQ,QAAQ,EAAE,WAAW,EAAE,UAAU;AAC7C,UAAE,eAAe;AACjB,gBAAQ,CAAC,SAAS,CAAC,IAAI;AAAA,MACzB;AACA,UAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,IACvC;AACA,UAAM,SAAU,MAAM,QAAQ,IAAI;AAClC,UAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,aAAS,iBAAiB,WAAW,KAAK;AAC1C,aAAS,iBAAiB,aAAa,MAAM;AAC7C,aAAS,iBAAiB,cAAc,OAAO;AAC/C,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,KAAK;AAC7C,eAAS,oBAAoB,aAAa,MAAM;AAChD,eAAS,oBAAoB,cAAc,OAAO;AAAA,IACpD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,MAAM;AAAA,IACvB,CAAC,QAAoB;AACnB,cAAQ,KAAK;AACb,UAAI,IAAI,KAAM,QAAO,KAAK,IAAI,IAAI;AAAA,UAC7B,KAAI,SAAS;AAAA,IACpB;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SACE,4EACG;AAAA;AAAA,IAED,4CAAC,wCACE,kBACC,4EAEE;AAAA;AAAA,QAAC,4BAAO;AAAA,QAAP;AAAA,UACC,WAAU;AAAA,UACV,SAAS,EAAE,SAAS,EAAE;AAAA,UACtB,SAAS,EAAE,SAAS,EAAE;AAAA,UACtB,MAAM,EAAE,SAAS,EAAE;AAAA,UACnB,YAAY,EAAE,UAAU,IAAI;AAAA,UAE5B;AAAA;AAAA,cAAC;AAAA;AAAA,gBAAI,WAAU;AAAA,gBACV,OAAO,EAAE,SAAS,KAAK;AAAA;AAAA,YAAG;AAAA,YAC/B;AAAA,cAAC;AAAA;AAAA,gBAAI,WAAU;AAAA,gBACV,OAAO,EAAE,SAAS,IAAI;AAAA;AAAA,YAAG;AAAA,YAC9B;AAAA,cAAC;AAAA;AAAA,gBAAI,WAAU;AAAA,gBACV,OAAO,EAAE,SAAS,KAAK;AAAA;AAAA,YAAG;AAAA,YAC/B;AAAA,cAAC;AAAA;AAAA,gBAAI,WAAU;AAAA,gBACV,OAAO,EAAE,SAAS,KAAK;AAAA;AAAA,YAAG;AAAA;AAAA;AAAA,MACjC;AAAA,MAGA;AAAA,QAAC,4BAAO;AAAA,QAAP;AAAA,UACC,WAAU;AAAA,UACV,SAAS,EAAE,SAAS,EAAE;AAAA,UACtB,SAAS,EAAE,SAAS,EAAE;AAAA,UACtB,MAAM,EAAE,SAAS,EAAE;AAAA,UACnB,YAAY,EAAE,UAAU,KAAK;AAAA,UAC7B,SAAS,MAAM,QAAQ,KAAK;AAAA;AAAA,MAC9B;AAAA,MAGA;AAAA,QAAC,4BAAO;AAAA,QAAP;AAAA,UACC,WAAU;AAAA,UACV,SAAS,EAAE,SAAS,GAAG,OAAO,KAAK;AAAA,UACnC,SAAS,EAAE,SAAS,GAAG,OAAO,EAAE;AAAA,UAChC,MAAM,EAAE,SAAS,GAAG,OAAO,KAAK;AAAA,UAChC,YAAY,EAAE,UAAU,MAAM,MAAM,UAAU;AAAA,UAC9C,SAAS,MAAM,QAAQ,KAAK;AAAA,UAE5B;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,cAElC,uDAAC,uBAAQ,cAAY,MAAC,OAAM,wBAE1B;AAAA,6DAAC,SAAI,WAAU,iEACb;AAAA,8DAAC,8BAAO,WAAU,oDAAmD;AAAA,kBACrE;AAAA,oBAAC,oBAAQ;AAAA,oBAAR;AAAA,sBACC,KAAK;AAAA,sBACL,aAAY;AAAA,sBACZ,WAAU;AAAA;AAAA,kBACZ;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAS,MAAM,QAAQ,KAAK;AAAA,sBAC5B,WAAU;AAAA,sBACV,cAAW;AAAA,sBAEX,sDAAC,yBAAE,WAAU,iCAAgC;AAAA;AAAA,kBAC/C;AAAA,mBACF;AAAA,gBAGA,6CAAC,oBAAQ,MAAR,EAAa,WAAU,oCACtB;AAAA,8DAAC,oBAAQ,OAAR,EAAc,WAAU,kDAAiD,+BAE1E;AAAA,kBAEC,SAAS,IAAI,CAAC,OAAO,MAAM;AAC1B,0BAAM,UAAU,CAAC,MAAM;AACvB,2BACA,6CAAC,MAAM,UAAN,EACE;AAAA,0BAAI,KACH,4CAAC,oBAAQ,WAAR,EAAkB,WAAU,4BAA2B;AAAA,sBAE1D;AAAA,wBAAC,oBAAQ;AAAA,wBAAR;AAAA,0BACC,SAAS,MAAM;AAAA,0BACf,eAAW;AAAA,4BACT;AAAA,4BACA;AAAA,4BACA;AAAA,4BACA;AAAA,4BACA;AAAA,4BACA;AAAA,4BACA;AAAA,4BACA;AAAA,0BACF;AAAA,0BAEC,gBAAM,MAAM,IAAI,CAAC,SAChB;AAAA,4BAAC,oBAAQ;AAAA,4BAAR;AAAA,8BAEC,OAAO,CAAC,KAAK,OAAO,GAAI,KAAK,YAAY,CAAC,CAAE,EAAE,KAAK,GAAG;AAAA,8BACtD,UAAU,MAAM,WAAW,IAAI;AAAA,8BAC/B,eAAW;AAAA,gCACT;AAAA,gCACA;AAAA,gCACA;AAAA,gCACA,UACI,0CACA;AAAA,8BACN;AAAA,8BAEC;AAAA,0CACC,4CAAC,UAAK,WAAU,qIACd,sDAAC,KAAK,MAAL,EAAU,WAAU,gGAA+F,GACtH,IAEA,4CAAC,KAAK,MAAL,EAAU,WAAU,qGAAoG;AAAA,gCAE3H,4CAAC,UAAK,WAAU,mBAAmB,eAAK,OAAM;AAAA,gCAC9C,4CAAC,kCAAW,WAAU,4GAA2G;AAAA;AAAA;AAAA,4BApB5H,KAAK;AAAA,0BAqBZ,CACD;AAAA;AAAA,sBACH;AAAA,yBA1CmB,MAAM,WAAW,aAAa,CAAC,EA2CpD;AAAA,kBAEF,CAAC;AAAA,mBACH;AAAA,gBAEC,eACC,4CAAC,SAAI,WAAU,wDACZ,uBACH;AAAA,gBAIF,6CAAC,SAAI,WAAU,+EACZ;AAAA,gCACC,4CAAC,UAAK,WAAU,6CAA4C,uBAAS;AAAA,kBAEvE,4CAAC,SAAI,WAAU,2DAA0D,qBAAE;AAAA,mBAC7E;AAAA,iBACF;AAAA;AAAA,UACF;AAAA;AAAA,MACF;AAAA,OACF,GAEJ;AAAA,KACF;AAEJ;","names":[]}
@@ -13,7 +13,12 @@ interface NavCommand {
13
13
  keywords?: string[];
14
14
  }
15
15
  interface NavCommandGroup {
16
- heading: string;
16
+ /**
17
+ * Optional group heading. Omit it for the primary group so its items
18
+ * read as the top-level menu — they render first, emphasized, with no
19
+ * label, separated from the headed groups below.
20
+ */
21
+ heading?: string;
17
22
  items: NavCommand[];
18
23
  }
19
24
  interface NavCommandMenuProps {
@@ -30,11 +35,17 @@ interface NavCommandMenuProps {
30
35
  * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.
31
36
  */
32
37
  accountSlot?: React.ReactNode;
38
+ /**
39
+ * Optional control rendered in the footer row (e.g. a theme toggle).
40
+ * Apps own theme state (next-themes etc.) so the shared nav stays
41
+ * framework-agnostic — same pattern as `accountSlot`.
42
+ */
43
+ footerSlot?: React.ReactNode;
33
44
  }
34
45
  declare function useNavCommandMenu(): {
35
46
  open: () => boolean;
36
47
  close: () => boolean;
37
48
  };
38
- declare function NavCommandMenu({ commands, trigger, accountSlot }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
49
+ declare function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
39
50
 
40
51
  export { type NavCommand, type NavCommandGroup, NavCommandMenu, type NavCommandMenuProps, useNavCommandMenu };
@@ -13,7 +13,12 @@ interface NavCommand {
13
13
  keywords?: string[];
14
14
  }
15
15
  interface NavCommandGroup {
16
- heading: string;
16
+ /**
17
+ * Optional group heading. Omit it for the primary group so its items
18
+ * read as the top-level menu — they render first, emphasized, with no
19
+ * label, separated from the headed groups below.
20
+ */
21
+ heading?: string;
17
22
  items: NavCommand[];
18
23
  }
19
24
  interface NavCommandMenuProps {
@@ -30,11 +35,17 @@ interface NavCommandMenuProps {
30
35
  * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.
31
36
  */
32
37
  accountSlot?: React.ReactNode;
38
+ /**
39
+ * Optional control rendered in the footer row (e.g. a theme toggle).
40
+ * Apps own theme state (next-themes etc.) so the shared nav stays
41
+ * framework-agnostic — same pattern as `accountSlot`.
42
+ */
43
+ footerSlot?: React.ReactNode;
33
44
  }
34
45
  declare function useNavCommandMenu(): {
35
46
  open: () => boolean;
36
47
  close: () => boolean;
37
48
  };
38
- declare function NavCommandMenu({ commands, trigger, accountSlot }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
49
+ declare function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
39
50
 
40
51
  export { type NavCommand, type NavCommandGroup, NavCommandMenu, type NavCommandMenuProps, useNavCommandMenu };
@@ -14,7 +14,7 @@ function useNavCommandMenu() {
14
14
  close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE))
15
15
  };
16
16
  }
17
- function NavCommandMenu({ commands, trigger, accountSlot }) {
17
+ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
18
18
  const [open, setOpen] = React.useState(false);
19
19
  const router = useRouter();
20
20
  const inputRef = React.useRef(null);
@@ -119,14 +119,14 @@ function NavCommandMenu({ commands, trigger, accountSlot }) {
119
119
  className: "w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl",
120
120
  onClick: (e) => e.stopPropagation(),
121
121
  children: /* @__PURE__ */ jsxs(Command, { shouldFilter: true, label: "Medialane navigation", children: [
122
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-border/40", children: [
123
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
122
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3.5 border-b border-border/40", children: [
123
+ /* @__PURE__ */ jsx(Search, { className: "h-[18px] w-[18px] text-muted-foreground shrink-0" }),
124
124
  /* @__PURE__ */ jsx(
125
125
  Command.Input,
126
126
  {
127
127
  ref: inputRef,
128
128
  placeholder: "Type a command or search\u2026",
129
- className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
129
+ className: "flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground"
130
130
  }
131
131
  ),
132
132
  /* @__PURE__ */ jsx(
@@ -141,45 +141,52 @@ function NavCommandMenu({ commands, trigger, accountSlot }) {
141
141
  ] }),
142
142
  /* @__PURE__ */ jsxs(Command.List, { className: "max-h-[60vh] overflow-y-auto p-2", children: [
143
143
  /* @__PURE__ */ jsx(Command.Empty, { className: "py-8 text-center text-sm text-muted-foreground", children: "No results found." }),
144
- commands.map((group, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
145
- i > 0 && /* @__PURE__ */ jsx(Command.Separator, { className: "my-1 h-px bg-border/40" }),
146
- /* @__PURE__ */ jsx(
147
- Command.Group,
148
- {
149
- heading: group.heading,
150
- className: cn(
151
- "[&_[cmdk-group-heading]]:px-2",
152
- "[&_[cmdk-group-heading]]:py-1.5",
153
- "[&_[cmdk-group-heading]]:text-xs",
154
- "[&_[cmdk-group-heading]]:font-medium",
155
- "[&_[cmdk-group-heading]]:text-muted-foreground"
156
- ),
157
- children: group.items.map((item) => /* @__PURE__ */ jsxs(
158
- Command.Item,
159
- {
160
- value: [item.label, ...item.keywords ?? []].join(" "),
161
- onSelect: () => runCommand(item),
162
- className: cn(
163
- "flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm cursor-pointer",
164
- "transition-colors",
165
- "aria-selected:bg-muted/60"
166
- ),
167
- children: [
168
- /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
169
- /* @__PURE__ */ jsx("span", { className: "flex-1", children: item.label }),
170
- /* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5 text-muted-foreground/40 shrink-0" })
171
- ]
172
- },
173
- item.id
174
- ))
175
- }
176
- )
177
- ] }, group.heading))
144
+ commands.map((group, i) => {
145
+ const primary = !group.heading;
146
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
147
+ i > 0 && /* @__PURE__ */ jsx(Command.Separator, { className: "my-1.5 h-px bg-border/40" }),
148
+ /* @__PURE__ */ jsx(
149
+ Command.Group,
150
+ {
151
+ heading: group.heading,
152
+ className: cn(
153
+ "[&_[cmdk-group-heading]]:px-2",
154
+ "[&_[cmdk-group-heading]]:pt-1.5",
155
+ "[&_[cmdk-group-heading]]:pb-1",
156
+ "[&_[cmdk-group-heading]]:text-[11px]",
157
+ "[&_[cmdk-group-heading]]:font-semibold",
158
+ "[&_[cmdk-group-heading]]:uppercase",
159
+ "[&_[cmdk-group-heading]]:tracking-wider",
160
+ "[&_[cmdk-group-heading]]:text-muted-foreground/70"
161
+ ),
162
+ children: group.items.map((item) => /* @__PURE__ */ jsxs(
163
+ Command.Item,
164
+ {
165
+ value: [item.label, ...item.keywords ?? []].join(" "),
166
+ onSelect: () => runCommand(item),
167
+ className: cn(
168
+ "group/item flex items-center gap-3 rounded-xl cursor-pointer",
169
+ "transition-colors duration-150",
170
+ "aria-selected:bg-primary/10",
171
+ primary ? "px-2.5 py-2.5 text-[15px] font-medium" : "px-3 py-2 text-sm"
172
+ ),
173
+ children: [
174
+ primary ? /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-muted/50 group-aria-selected/item:bg-primary/15 transition-colors shrink-0", children: /* @__PURE__ */ jsx(item.icon, { className: "h-[18px] w-[18px] text-foreground/80 group-aria-selected/item:text-primary transition-colors" }) }) : /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 text-muted-foreground group-aria-selected/item:text-foreground transition-colors shrink-0" }),
175
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label }),
176
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5 text-muted-foreground/30 group-aria-selected/item:text-primary/70 transition-colors shrink-0" })
177
+ ]
178
+ },
179
+ item.id
180
+ ))
181
+ }
182
+ )
183
+ ] }, group.heading ?? `__primary-${i}`);
184
+ })
178
185
  ] }),
179
186
  accountSlot && /* @__PURE__ */ jsx("div", { className: "border-t border-border/40 bg-background/40 px-3 py-3", children: accountSlot }),
180
- /* @__PURE__ */ jsxs("div", { className: "px-4 py-2.5 border-t border-border/40 flex items-center justify-between", children: [
181
- /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/50", children: "medialane" }),
182
- /* @__PURE__ */ jsx("kbd", { className: "text-[10px] text-muted-foreground/50 font-mono", children: "\u2318K" })
187
+ /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 border-t border-border/40 flex items-center justify-between gap-3", children: [
188
+ footerSlot ?? /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/50 pl-1", children: "medialane" }),
189
+ /* @__PURE__ */ jsx("kbd", { className: "text-[10px] text-muted-foreground/50 font-mono shrink-0", children: "\u2318K" })
183
190
  ] })
184
191
  ] })
185
192
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n}\n\nexport interface NavCommandGroup {\n heading: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Aurora blobs — vivid intensity */}\n <motion.div\n className=\"nav-canvas-aurora\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"aurora-purple animate-blob w-[60vw] h-[60vw] -top-[10vw] -left-[10vw]\"\n style={{ opacity: 0.25 }} />\n <div className=\"aurora-blue animate-blob-slow w-[50vw] h-[50vw] -top-[5vw] -right-[10vw]\"\n style={{ opacity: 0.2 }} />\n <div className=\"aurora-rose animate-blob w-[45vw] h-[45vw] bottom-[5vw] -left-[5vw]\"\n style={{ opacity: 0.15 }} />\n <div className=\"aurora-orange animate-blob-slow w-[40vw] h-[40vw] -bottom-[5vw] -right-[5vw]\"\n style={{ opacity: 0.15 }} />\n </motion.div>\n\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-center justify-center p-4\"\n initial={{ opacity: 0, scale: 0.97 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.97 }}\n transition={{ duration: 0.15, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className=\"w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\">\n {/* Search bar */}\n <div className=\"flex items-center gap-3 px-4 py-3 border-b border-border/40\">\n <Search className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <Command.Input\n ref={inputRef}\n placeholder=\"Type a command or search…\"\n className=\"flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground\"\n />\n <button\n onClick={() => setOpen(false)}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"max-h-[60vh] overflow-y-auto p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => (\n <React.Fragment key={group.heading}>\n {i > 0 && (\n <Command.Separator className=\"my-1 h-px bg-border/40\" />\n )}\n <Command.Group\n heading={group.heading}\n className={cn(\n \"[&_[cmdk-group-heading]]:px-2\",\n \"[&_[cmdk-group-heading]]:py-1.5\",\n \"[&_[cmdk-group-heading]]:text-xs\",\n \"[&_[cmdk-group-heading]]:font-medium\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground\"\n )}\n >\n {group.items.map((item) => (\n <Command.Item\n key={item.id}\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={() => runCommand(item)}\n className={cn(\n \"flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm cursor-pointer\",\n \"transition-colors\",\n \"aria-selected:bg-muted/60\"\n )}\n >\n <item.icon className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <span className=\"flex-1\">{item.label}</span>\n <ArrowRight className=\"h-3.5 w-3.5 text-muted-foreground/40 shrink-0\" />\n </Command.Item>\n ))}\n </Command.Group>\n </React.Fragment>\n ))}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-background/40 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"px-4 py-2.5 border-t border-border/40 flex items-center justify-between\">\n <span className=\"text-[10px] text-muted-foreground/50\">medialane</span>\n <kbd className=\"text-[10px] text-muted-foreground/50 font-mono\">⌘K</kbd>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Aurora blobs — vivid intensity */}\n <motion.div\n className=\"nav-canvas-aurora\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"aurora-purple animate-blob w-[60vw] h-[60vw] -top-[10vw] -left-[10vw]\"\n style={{ opacity: 0.25 }} />\n <div className=\"aurora-blue animate-blob-slow w-[50vw] h-[50vw] -top-[5vw] -right-[10vw]\"\n style={{ opacity: 0.2 }} />\n <div className=\"aurora-rose animate-blob w-[45vw] h-[45vw] bottom-[5vw] -left-[5vw]\"\n style={{ opacity: 0.15 }} />\n <div className=\"aurora-orange animate-blob-slow w-[40vw] h-[40vw] -bottom-[5vw] -right-[5vw]\"\n style={{ opacity: 0.15 }} />\n </motion.div>\n\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-center justify-center p-4\"\n initial={{ opacity: 0, scale: 0.97 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.97 }}\n transition={{ duration: 0.15, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className=\"w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\">\n {/* Search bar */}\n <div className=\"flex items-center gap-3 px-4 py-3.5 border-b border-border/40\">\n <Search className=\"h-[18px] w-[18px] text-muted-foreground shrink-0\" />\n <Command.Input\n ref={inputRef}\n placeholder=\"Type a command or search…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <button\n onClick={() => setOpen(false)}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"max-h-[60vh] overflow-y-auto p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group\n heading={group.heading}\n className={cn(\n \"[&_[cmdk-group-heading]]:px-2\",\n \"[&_[cmdk-group-heading]]:pt-1.5\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[11px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-wider\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/70\"\n )}\n >\n {group.items.map((item) => (\n <Command.Item\n key={item.id}\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={() => runCommand(item)}\n className={cn(\n \"group/item flex items-center gap-3 rounded-xl cursor-pointer\",\n \"transition-colors duration-150\",\n \"aria-selected:bg-primary/10\",\n primary\n ? \"px-2.5 py-2.5 text-[15px] font-medium\"\n : \"px-3 py-2 text-sm\"\n )}\n >\n {primary ? (\n <span className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-muted/50 group-aria-selected/item:bg-primary/15 transition-colors shrink-0\">\n <item.icon className=\"h-[18px] w-[18px] text-foreground/80 group-aria-selected/item:text-primary transition-colors\" />\n </span>\n ) : (\n <item.icon className=\"h-4 w-4 text-muted-foreground group-aria-selected/item:text-foreground transition-colors shrink-0\" />\n )}\n <span className=\"flex-1 truncate\">{item.label}</span>\n <ArrowRight className=\"h-3.5 w-3.5 text-muted-foreground/30 group-aria-selected/item:text-primary/70 transition-colors shrink-0\" />\n </Command.Item>\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-background/40 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"px-3 py-2 border-t border-border/40 flex items-center justify-between gap-3\">\n {footerSlot ?? (\n <span className=\"text-[10px] text-muted-foreground/50 pl-1\">medialane</span>\n )}\n <kbd className=\"text-[10px] text-muted-foreground/50 font-mono shrink-0\">⌘K</kbd>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var portfolio_subnav_exports = {};
31
+ __export(portfolio_subnav_exports, {
32
+ PortfolioSubnav: () => PortfolioSubnav
33
+ });
34
+ module.exports = __toCommonJS(portfolio_subnav_exports);
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var import_link = __toESM(require("next/link"), 1);
37
+ var import_cn = require("../utils/cn.js");
38
+ const BADGE_VARIANT_CLASS = {
39
+ destructive: "bg-destructive text-destructive-foreground",
40
+ primary: "bg-primary text-primary-foreground",
41
+ warning: "bg-amber-500 text-white"
42
+ };
43
+ function PortfolioSubnav({
44
+ groups,
45
+ pathname,
46
+ badgeCounts = {},
47
+ className
48
+ }) {
49
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
50
+ "nav",
51
+ {
52
+ className: (0, import_cn.cn)(
53
+ "overflow-x-auto scrollbar-hide -mx-4 px-4 sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8 border-b border-border/60",
54
+ className
55
+ ),
56
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-center min-w-max gap-0", children: groups.map((group, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center", children: [
57
+ group.items.map((item) => {
58
+ const active = pathname === item.href || pathname.startsWith(item.href + "/");
59
+ const count = item.badge ? badgeCounts[item.badge.key] ?? 0 : 0;
60
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
61
+ import_link.default,
62
+ {
63
+ href: item.href,
64
+ className: (0, import_cn.cn)(
65
+ "relative flex items-center gap-1.5 px-3 py-2.5 text-sm whitespace-nowrap transition-colors shrink-0 border-b-2 min-h-10",
66
+ active ? "border-primary text-foreground font-medium" : "border-transparent text-muted-foreground hover:text-foreground"
67
+ ),
68
+ children: [
69
+ item.label,
70
+ item.badge && count > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
71
+ "span",
72
+ {
73
+ className: (0, import_cn.cn)(
74
+ "h-4 min-w-4 rounded-full text-[10px] font-bold flex items-center justify-center px-1",
75
+ BADGE_VARIANT_CLASS[item.badge.variant ?? "primary"]
76
+ ),
77
+ children: count
78
+ }
79
+ )
80
+ ]
81
+ },
82
+ item.href
83
+ );
84
+ }),
85
+ groupIndex < groups.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-px h-4 bg-border/40 mx-1 self-center shrink-0" })
86
+ ] }, group.label)) })
87
+ }
88
+ );
89
+ }
90
+ // Annotate the CommonJS export names for ESM import in node:
91
+ 0 && (module.exports = {
92
+ PortfolioSubnav
93
+ });
94
+ //# sourceMappingURL=portfolio-subnav.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/portfolio-subnav.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { cn } from \"../utils/cn.js\";\n\nexport type PortfolioBadgeVariant = \"destructive\" | \"primary\" | \"warning\";\n\nexport interface PortfolioNavItem {\n label: string;\n href: string;\n /** Optional count badge. `key` indexes `badgeCounts`; `variant` sets color. */\n badge?: { key: string; variant?: PortfolioBadgeVariant };\n}\n\nexport interface PortfolioNavGroup {\n label: string;\n items: PortfolioNavItem[];\n}\n\nexport interface PortfolioSubnavProps {\n groups: PortfolioNavGroup[];\n /** Current pathname — caller passes `usePathname()`. */\n pathname: string;\n /** Badge counts keyed by `item.badge.key`. A badge renders only when > 0. */\n badgeCounts?: Record<string, number>;\n className?: string;\n}\n\nconst BADGE_VARIANT_CLASS: Record<PortfolioBadgeVariant, string> = {\n destructive: \"bg-destructive text-destructive-foreground\",\n primary: \"bg-primary text-primary-foreground\",\n warning: \"bg-amber-500 text-white\",\n};\n\nexport function PortfolioSubnav({\n groups,\n pathname,\n badgeCounts = {},\n className,\n}: PortfolioSubnavProps) {\n return (\n <nav\n className={cn(\n \"overflow-x-auto scrollbar-hide -mx-4 px-4 sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8 border-b border-border/60\",\n className,\n )}\n >\n <div className=\"flex items-center min-w-max gap-0\">\n {groups.map((group, groupIndex) => (\n <div key={group.label} className=\"flex items-center\">\n {group.items.map((item) => {\n const active =\n pathname === item.href || pathname.startsWith(item.href + \"/\");\n const count = item.badge ? badgeCounts[item.badge.key] ?? 0 : 0;\n return (\n <Link\n key={item.href}\n href={item.href}\n className={cn(\n \"relative flex items-center gap-1.5 px-3 py-2.5 text-sm whitespace-nowrap transition-colors shrink-0 border-b-2 min-h-10\",\n active\n ? \"border-primary text-foreground font-medium\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {item.label}\n {item.badge && count > 0 && (\n <span\n className={cn(\n \"h-4 min-w-4 rounded-full text-[10px] font-bold flex items-center justify-center px-1\",\n BADGE_VARIANT_CLASS[item.badge.variant ?? \"primary\"],\n )}\n >\n {count}\n </span>\n )}\n </Link>\n );\n })}\n {groupIndex < groups.length - 1 && (\n <span className=\"w-px h-4 bg-border/40 mx-1 self-center shrink-0\" />\n )}\n </div>\n ))}\n </div>\n </nav>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuDgB;AArDhB,kBAAiB;AACjB,gBAAmB;AAyBnB,MAAM,sBAA6D;AAAA,EACjE,aAAa;AAAA,EACb,SAAS;AAAA,EACT,SAAS;AACX;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,cAAc,CAAC;AAAA,EACf;AACF,GAAyB;AACvB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA,sDAAC,SAAI,WAAU,qCACZ,iBAAO,IAAI,CAAC,OAAO,eAClB,6CAAC,SAAsB,WAAU,qBAC9B;AAAA,cAAM,MAAM,IAAI,CAAC,SAAS;AACzB,gBAAM,SACJ,aAAa,KAAK,QAAQ,SAAS,WAAW,KAAK,OAAO,GAAG;AAC/D,gBAAM,QAAQ,KAAK,QAAQ,YAAY,KAAK,MAAM,GAAG,KAAK,IAAI;AAC9D,iBACE;AAAA,YAAC,YAAAA;AAAA,YAAA;AAAA,cAEC,MAAM,KAAK;AAAA,cACX,eAAW;AAAA,gBACT;AAAA,gBACA,SACI,+CACA;AAAA,cACN;AAAA,cAEC;AAAA,qBAAK;AAAA,gBACL,KAAK,SAAS,QAAQ,KACrB;AAAA,kBAAC;AAAA;AAAA,oBACC,eAAW;AAAA,sBACT;AAAA,sBACA,oBAAoB,KAAK,MAAM,WAAW,SAAS;AAAA,oBACrD;AAAA,oBAEC;AAAA;AAAA,gBACH;AAAA;AAAA;AAAA,YAlBG,KAAK;AAAA,UAoBZ;AAAA,QAEJ,CAAC;AAAA,QACA,aAAa,OAAO,SAAS,KAC5B,4CAAC,UAAK,WAAU,mDAAkD;AAAA,WA/B5D,MAAM,KAiChB,CACD,GACH;AAAA;AAAA,EACF;AAEJ;","names":["Link"]}
@@ -0,0 +1,27 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type PortfolioBadgeVariant = "destructive" | "primary" | "warning";
4
+ interface PortfolioNavItem {
5
+ label: string;
6
+ href: string;
7
+ /** Optional count badge. `key` indexes `badgeCounts`; `variant` sets color. */
8
+ badge?: {
9
+ key: string;
10
+ variant?: PortfolioBadgeVariant;
11
+ };
12
+ }
13
+ interface PortfolioNavGroup {
14
+ label: string;
15
+ items: PortfolioNavItem[];
16
+ }
17
+ interface PortfolioSubnavProps {
18
+ groups: PortfolioNavGroup[];
19
+ /** Current pathname — caller passes `usePathname()`. */
20
+ pathname: string;
21
+ /** Badge counts keyed by `item.badge.key`. A badge renders only when > 0. */
22
+ badgeCounts?: Record<string, number>;
23
+ className?: string;
24
+ }
25
+ declare function PortfolioSubnav({ groups, pathname, badgeCounts, className, }: PortfolioSubnavProps): react_jsx_runtime.JSX.Element;
26
+
27
+ export { type PortfolioBadgeVariant, type PortfolioNavGroup, type PortfolioNavItem, PortfolioSubnav, type PortfolioSubnavProps };
@@ -0,0 +1,27 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type PortfolioBadgeVariant = "destructive" | "primary" | "warning";
4
+ interface PortfolioNavItem {
5
+ label: string;
6
+ href: string;
7
+ /** Optional count badge. `key` indexes `badgeCounts`; `variant` sets color. */
8
+ badge?: {
9
+ key: string;
10
+ variant?: PortfolioBadgeVariant;
11
+ };
12
+ }
13
+ interface PortfolioNavGroup {
14
+ label: string;
15
+ items: PortfolioNavItem[];
16
+ }
17
+ interface PortfolioSubnavProps {
18
+ groups: PortfolioNavGroup[];
19
+ /** Current pathname — caller passes `usePathname()`. */
20
+ pathname: string;
21
+ /** Badge counts keyed by `item.badge.key`. A badge renders only when > 0. */
22
+ badgeCounts?: Record<string, number>;
23
+ className?: string;
24
+ }
25
+ declare function PortfolioSubnav({ groups, pathname, badgeCounts, className, }: PortfolioSubnavProps): react_jsx_runtime.JSX.Element;
26
+
27
+ export { type PortfolioBadgeVariant, type PortfolioNavGroup, type PortfolioNavItem, PortfolioSubnav, type PortfolioSubnavProps };
@@ -0,0 +1,60 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import Link from "next/link";
4
+ import { cn } from "../utils/cn.js";
5
+ const BADGE_VARIANT_CLASS = {
6
+ destructive: "bg-destructive text-destructive-foreground",
7
+ primary: "bg-primary text-primary-foreground",
8
+ warning: "bg-amber-500 text-white"
9
+ };
10
+ function PortfolioSubnav({
11
+ groups,
12
+ pathname,
13
+ badgeCounts = {},
14
+ className
15
+ }) {
16
+ return /* @__PURE__ */ jsx(
17
+ "nav",
18
+ {
19
+ className: cn(
20
+ "overflow-x-auto scrollbar-hide -mx-4 px-4 sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8 border-b border-border/60",
21
+ className
22
+ ),
23
+ children: /* @__PURE__ */ jsx("div", { className: "flex items-center min-w-max gap-0", children: groups.map((group, groupIndex) => /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
24
+ group.items.map((item) => {
25
+ const active = pathname === item.href || pathname.startsWith(item.href + "/");
26
+ const count = item.badge ? badgeCounts[item.badge.key] ?? 0 : 0;
27
+ return /* @__PURE__ */ jsxs(
28
+ Link,
29
+ {
30
+ href: item.href,
31
+ className: cn(
32
+ "relative flex items-center gap-1.5 px-3 py-2.5 text-sm whitespace-nowrap transition-colors shrink-0 border-b-2 min-h-10",
33
+ active ? "border-primary text-foreground font-medium" : "border-transparent text-muted-foreground hover:text-foreground"
34
+ ),
35
+ children: [
36
+ item.label,
37
+ item.badge && count > 0 && /* @__PURE__ */ jsx(
38
+ "span",
39
+ {
40
+ className: cn(
41
+ "h-4 min-w-4 rounded-full text-[10px] font-bold flex items-center justify-center px-1",
42
+ BADGE_VARIANT_CLASS[item.badge.variant ?? "primary"]
43
+ ),
44
+ children: count
45
+ }
46
+ )
47
+ ]
48
+ },
49
+ item.href
50
+ );
51
+ }),
52
+ groupIndex < groups.length - 1 && /* @__PURE__ */ jsx("span", { className: "w-px h-4 bg-border/40 mx-1 self-center shrink-0" })
53
+ ] }, group.label)) })
54
+ }
55
+ );
56
+ }
57
+ export {
58
+ PortfolioSubnav
59
+ };
60
+ //# sourceMappingURL=portfolio-subnav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/portfolio-subnav.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { cn } from \"../utils/cn.js\";\n\nexport type PortfolioBadgeVariant = \"destructive\" | \"primary\" | \"warning\";\n\nexport interface PortfolioNavItem {\n label: string;\n href: string;\n /** Optional count badge. `key` indexes `badgeCounts`; `variant` sets color. */\n badge?: { key: string; variant?: PortfolioBadgeVariant };\n}\n\nexport interface PortfolioNavGroup {\n label: string;\n items: PortfolioNavItem[];\n}\n\nexport interface PortfolioSubnavProps {\n groups: PortfolioNavGroup[];\n /** Current pathname — caller passes `usePathname()`. */\n pathname: string;\n /** Badge counts keyed by `item.badge.key`. A badge renders only when > 0. */\n badgeCounts?: Record<string, number>;\n className?: string;\n}\n\nconst BADGE_VARIANT_CLASS: Record<PortfolioBadgeVariant, string> = {\n destructive: \"bg-destructive text-destructive-foreground\",\n primary: \"bg-primary text-primary-foreground\",\n warning: \"bg-amber-500 text-white\",\n};\n\nexport function PortfolioSubnav({\n groups,\n pathname,\n badgeCounts = {},\n className,\n}: PortfolioSubnavProps) {\n return (\n <nav\n className={cn(\n \"overflow-x-auto scrollbar-hide -mx-4 px-4 sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8 border-b border-border/60\",\n className,\n )}\n >\n <div className=\"flex items-center min-w-max gap-0\">\n {groups.map((group, groupIndex) => (\n <div key={group.label} className=\"flex items-center\">\n {group.items.map((item) => {\n const active =\n pathname === item.href || pathname.startsWith(item.href + \"/\");\n const count = item.badge ? badgeCounts[item.badge.key] ?? 0 : 0;\n return (\n <Link\n key={item.href}\n href={item.href}\n className={cn(\n \"relative flex items-center gap-1.5 px-3 py-2.5 text-sm whitespace-nowrap transition-colors shrink-0 border-b-2 min-h-10\",\n active\n ? \"border-primary text-foreground font-medium\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {item.label}\n {item.badge && count > 0 && (\n <span\n className={cn(\n \"h-4 min-w-4 rounded-full text-[10px] font-bold flex items-center justify-center px-1\",\n BADGE_VARIANT_CLASS[item.badge.variant ?? \"primary\"],\n )}\n >\n {count}\n </span>\n )}\n </Link>\n );\n })}\n {groupIndex < groups.length - 1 && (\n <span className=\"w-px h-4 bg-border/40 mx-1 self-center shrink-0\" />\n )}\n </div>\n ))}\n </div>\n </nav>\n );\n}\n"],"mappings":";AAuDgB,SAYI,KAZJ;AArDhB,OAAO,UAAU;AACjB,SAAS,UAAU;AAyBnB,MAAM,sBAA6D;AAAA,EACjE,aAAa;AAAA,EACb,SAAS;AAAA,EACT,SAAS;AACX;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,cAAc,CAAC;AAAA,EACf;AACF,GAAyB;AACvB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA,8BAAC,SAAI,WAAU,qCACZ,iBAAO,IAAI,CAAC,OAAO,eAClB,qBAAC,SAAsB,WAAU,qBAC9B;AAAA,cAAM,MAAM,IAAI,CAAC,SAAS;AACzB,gBAAM,SACJ,aAAa,KAAK,QAAQ,SAAS,WAAW,KAAK,OAAO,GAAG;AAC/D,gBAAM,QAAQ,KAAK,QAAQ,YAAY,KAAK,MAAM,GAAG,KAAK,IAAI;AAC9D,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,MAAM,KAAK;AAAA,cACX,WAAW;AAAA,gBACT;AAAA,gBACA,SACI,+CACA;AAAA,cACN;AAAA,cAEC;AAAA,qBAAK;AAAA,gBACL,KAAK,SAAS,QAAQ,KACrB;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAW;AAAA,sBACT;AAAA,sBACA,oBAAoB,KAAK,MAAM,WAAW,SAAS;AAAA,oBACrD;AAAA,oBAEC;AAAA;AAAA,gBACH;AAAA;AAAA;AAAA,YAlBG,KAAK;AAAA,UAoBZ;AAAA,QAEJ,CAAC;AAAA,QACA,aAAa,OAAO,SAAS,KAC5B,oBAAC,UAAK,WAAU,mDAAkD;AAAA,WA/B5D,MAAM,KAiChB,CACD,GACH;AAAA;AAAA,EACF;AAEJ;","names":[]}
package/dist/index.cjs CHANGED
@@ -55,6 +55,7 @@ __export(index_exports, {
55
55
  MotionCard: () => import_motion_primitives.MotionCard,
56
56
  NavCommandMenu: () => import_nav_command_menu.NavCommandMenu,
57
57
  PageContainer: () => import_page_container.PageContainer,
58
+ PortfolioSubnav: () => import_portfolio_subnav.PortfolioSubnav,
58
59
  SPRING: () => import_motion_primitives.SPRING,
59
60
  ScrollSection: () => import_scroll_section.ScrollSection,
60
61
  ShareButton: () => import_share_button.ShareButton,
@@ -64,6 +65,7 @@ __export(index_exports, {
64
65
  TokenCard: () => import_token_card.TokenCard,
65
66
  TokenCardSkeleton: () => import_token_card.TokenCardSkeleton,
66
67
  cn: () => import_cn.cn,
68
+ derivePortfolioCounts: () => import_portfolio_counts.derivePortfolioCounts,
67
69
  formatDisplayPrice: () => import_format.formatDisplayPrice,
68
70
  ipfsToHttp: () => import_ipfs.ipfsToHttp,
69
71
  shortenAddress: () => import_address.shortenAddress,
@@ -105,6 +107,8 @@ var import_discover_feed_section = require("./components/discover-feed-section.j
105
107
  var import_launchpad_services = require("./components/launchpad-services.js");
106
108
  var import_launchpad_services2 = require("./data/launchpad-services.js");
107
109
  var import_nav_command_menu = require("./components/nav-command-menu.js");
110
+ var import_portfolio_subnav = require("./components/portfolio-subnav.js");
111
+ var import_portfolio_counts = require("./utils/portfolio-counts.js");
108
112
  // Annotate the CommonJS export names for ESM import in node:
109
113
  0 && (module.exports = {
110
114
  ACTIVITY_TYPE_CONFIG,
@@ -144,6 +148,7 @@ var import_nav_command_menu = require("./components/nav-command-menu.js");
144
148
  MotionCard,
145
149
  NavCommandMenu,
146
150
  PageContainer,
151
+ PortfolioSubnav,
147
152
  SPRING,
148
153
  ScrollSection,
149
154
  ShareButton,
@@ -153,6 +158,7 @@ var import_nav_command_menu = require("./components/nav-command-menu.js");
153
158
  TokenCard,
154
159
  TokenCardSkeleton,
155
160
  cn,
161
+ derivePortfolioCounts,
156
162
  formatDisplayPrice,
157
163
  ipfsToHttp,
158
164
  shortenAddress,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// ── Utils ─────────────────────────────────────────────────────────────────────\nexport { cn } from \"./utils/cn.js\";\nexport { formatDisplayPrice } from \"./utils/format.js\";\nexport { shortenAddress } from \"./utils/address.js\";\nexport { ipfsToHttp } from \"./utils/ipfs.js\";\n\n// ── Data (server-safe — no React, safe in Server Components) ──────────────────\nexport { IP_TYPE_DATA, IP_TYPE_DATA_MAP } from \"./data/ip-types.js\";\nexport type { IpTypeData } from \"./data/ip-types.js\";\nexport { BRAND } from \"./data/brand.js\";\n\n// ── Components (client-only — all have \"use client\") ─────────────────────────\nexport { CurrencyIcon, CurrencyAmount } from \"./components/currency-icon.js\";\nexport type { CurrencyIconProps, CurrencyAmountProps } from \"./components/currency-icon.js\";\n\nexport { IpTypeBadge, IP_TYPE_CONFIG, IP_TYPE_MAP } from \"./components/ip-type-badge.js\";\nexport type { IpTypeBadgeProps, IpTypeConfig } from \"./components/ip-type-badge.js\";\n\nexport { AddressDisplay } from \"./components/address-display.js\";\nexport type { AddressDisplayProps } from \"./components/address-display.js\";\n\nexport { MedialaneIcon } from \"./components/brand-icon.js\";\nexport type { MedialaneIconProps } from \"./components/brand-icon.js\";\nexport { MedialaneLogoFull } from \"./components/brand-logo.js\";\nexport type { MedialaneLogoFullProps } from \"./components/brand-logo.js\";\n\n// ── v0.2 additions ────────────────────────────────────────────────────────────\nexport { MotionCard, FadeIn, Stagger, StaggerItem, KineticWords, SPRING, EASE_OUT } from \"./components/motion-primitives.js\";\nexport { PageContainer } from \"./components/page-container.js\";\nexport type { PageContainerProps } from \"./components/page-container.js\";\nexport { ScrollSection } from \"./components/scroll-section.js\";\nexport type { ScrollSectionProps } from \"./components/scroll-section.js\";\nexport { ShareButton } from \"./components/share-button.js\";\nexport type { ShareButtonProps } from \"./components/share-button.js\";\nexport { CollectionCard, CollectionCardSkeleton } from \"./components/collection-card.js\";\nexport type { CollectionCardProps } from \"./components/collection-card.js\";\nexport { TokenCard, TokenCardSkeleton } from \"./components/token-card.js\";\nexport type { TokenCardProps, RarityTier } from \"./components/token-card.js\";\n\n// ── v0.3 additions ────────────────────────────────────────────────────────────\nexport { timeAgo } from \"./utils/time.js\";\nexport { ACTIVITY_TYPE_CONFIG, TYPE_FILTERS } from \"./data/activity.js\";\nexport type { ActivityTypeConfig } from \"./data/activity.js\";\nexport { HeroSlider, HeroSliderSkeleton } from \"./components/hero-slider.js\";\nexport type { HeroSliderProps } from \"./components/hero-slider.js\";\nexport { ActivityTicker } from \"./components/activity-ticker.js\";\nexport type { ActivityTickerProps } from \"./components/activity-ticker.js\";\nexport { ListingCard, ListingCardSkeleton } from \"./components/listing-card.js\";\nexport type { ListingCardProps } from \"./components/listing-card.js\";\nexport { ActivityRow } from \"./components/activity-row.js\";\nexport type { ActivityRowProps } from \"./components/activity-row.js\";\nexport { ActivityFeedShell } from \"./components/activity-feed-shell.js\";\nexport type { ActivityFeedShellProps } from \"./components/activity-feed-shell.js\";\nexport { LaunchpadGrid } from \"./components/launchpad-grid.js\";\nexport type { LaunchpadGridProps, FeatureItem } from \"./components/launchpad-grid.js\";\nexport { CtaCardGrid } from \"./components/cta-card-grid.js\";\nexport type { CtaCardGridProps, CtaCardItem } from \"./components/cta-card-grid.js\";\n\n// ── v0.3.2 additions ─────────────────────────────────────────────────────────\nexport { DiscoverHero } from \"./components/discover-hero.js\";\nexport type { DiscoverHeroProps } from \"./components/discover-hero.js\";\nexport { FeaturedCarousel, FeaturedCarouselSkeleton } from \"./components/featured-carousel.js\";\nexport type { FeaturedCarouselProps } from \"./components/featured-carousel.js\";\nexport { DiscoverCollectionsStrip } from \"./components/discover-collections-strip.js\";\nexport type { DiscoverCollectionsStripProps } from \"./components/discover-collections-strip.js\";\nexport { DiscoverCreatorsStrip } from \"./components/discover-creators-strip.js\";\nexport type { DiscoverCreatorsStripProps } from \"./components/discover-creators-strip.js\";\nexport { DiscoverFeedSection } from \"./components/discover-feed-section.js\";\nexport type { DiscoverFeedSectionProps } from \"./components/discover-feed-section.js\";\n\n// ── v0.4 additions ────────────────────────────────────────────────────────────\nexport { LaunchpadServicesGrid } from \"./components/launchpad-services.js\";\nexport type { LaunchpadServicesGridProps, ServiceCardProps } from \"./components/launchpad-services.js\";\nexport { LAUNCHPAD_SERVICE_DEFINITIONS } from \"./data/launchpad-services.js\";\nexport type { ServiceDefinition, ServiceStatus, ServiceCategory } from \"./data/launchpad-services.js\";\n\n// ── v0.5.0 additions ─────────────────────────────────────────────────────────\nexport { NavCommandMenu, useNavCommandMenu } from \"./components/nav-command-menu.js\";\nexport type { NavCommand, NavCommandGroup, NavCommandMenuProps } from \"./components/nav-command-menu.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,gBAAmB;AACnB,oBAAmC;AACnC,qBAA+B;AAC/B,kBAA2B;AAG3B,sBAA+C;AAE/C,mBAAsB;AAGtB,2BAA6C;AAG7C,2BAAyD;AAGzD,6BAA+B;AAG/B,wBAA8B;AAE9B,wBAAkC;AAIlC,+BAAyF;AACzF,4BAA8B;AAE9B,4BAA8B;AAE9B,0BAA4B;AAE5B,6BAAuD;AAEvD,wBAA6C;AAI7C,kBAAwB;AACxB,sBAAmD;AAEnD,yBAA+C;AAE/C,6BAA+B;AAE/B,0BAAiD;AAEjD,0BAA4B;AAE5B,iCAAkC;AAElC,4BAA8B;AAE9B,2BAA4B;AAI5B,2BAA6B;AAE7B,+BAA2D;AAE3D,wCAAyC;AAEzC,qCAAsC;AAEtC,mCAAoC;AAIpC,gCAAsC;AAEtC,IAAAA,6BAA8C;AAI9C,8BAAkD;","names":["import_launchpad_services"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// ── Utils ─────────────────────────────────────────────────────────────────────\nexport { cn } from \"./utils/cn.js\";\nexport { formatDisplayPrice } from \"./utils/format.js\";\nexport { shortenAddress } from \"./utils/address.js\";\nexport { ipfsToHttp } from \"./utils/ipfs.js\";\n\n// ── Data (server-safe — no React, safe in Server Components) ──────────────────\nexport { IP_TYPE_DATA, IP_TYPE_DATA_MAP } from \"./data/ip-types.js\";\nexport type { IpTypeData } from \"./data/ip-types.js\";\nexport { BRAND } from \"./data/brand.js\";\n\n// ── Components (client-only — all have \"use client\") ─────────────────────────\nexport { CurrencyIcon, CurrencyAmount } from \"./components/currency-icon.js\";\nexport type { CurrencyIconProps, CurrencyAmountProps } from \"./components/currency-icon.js\";\n\nexport { IpTypeBadge, IP_TYPE_CONFIG, IP_TYPE_MAP } from \"./components/ip-type-badge.js\";\nexport type { IpTypeBadgeProps, IpTypeConfig } from \"./components/ip-type-badge.js\";\n\nexport { AddressDisplay } from \"./components/address-display.js\";\nexport type { AddressDisplayProps } from \"./components/address-display.js\";\n\nexport { MedialaneIcon } from \"./components/brand-icon.js\";\nexport type { MedialaneIconProps } from \"./components/brand-icon.js\";\nexport { MedialaneLogoFull } from \"./components/brand-logo.js\";\nexport type { MedialaneLogoFullProps } from \"./components/brand-logo.js\";\n\n// ── v0.2 additions ────────────────────────────────────────────────────────────\nexport { MotionCard, FadeIn, Stagger, StaggerItem, KineticWords, SPRING, EASE_OUT } from \"./components/motion-primitives.js\";\nexport { PageContainer } from \"./components/page-container.js\";\nexport type { PageContainerProps } from \"./components/page-container.js\";\nexport { ScrollSection } from \"./components/scroll-section.js\";\nexport type { ScrollSectionProps } from \"./components/scroll-section.js\";\nexport { ShareButton } from \"./components/share-button.js\";\nexport type { ShareButtonProps } from \"./components/share-button.js\";\nexport { CollectionCard, CollectionCardSkeleton } from \"./components/collection-card.js\";\nexport type { CollectionCardProps } from \"./components/collection-card.js\";\nexport { TokenCard, TokenCardSkeleton } from \"./components/token-card.js\";\nexport type { TokenCardProps, RarityTier } from \"./components/token-card.js\";\n\n// ── v0.3 additions ────────────────────────────────────────────────────────────\nexport { timeAgo } from \"./utils/time.js\";\nexport { ACTIVITY_TYPE_CONFIG, TYPE_FILTERS } from \"./data/activity.js\";\nexport type { ActivityTypeConfig } from \"./data/activity.js\";\nexport { HeroSlider, HeroSliderSkeleton } from \"./components/hero-slider.js\";\nexport type { HeroSliderProps } from \"./components/hero-slider.js\";\nexport { ActivityTicker } from \"./components/activity-ticker.js\";\nexport type { ActivityTickerProps } from \"./components/activity-ticker.js\";\nexport { ListingCard, ListingCardSkeleton } from \"./components/listing-card.js\";\nexport type { ListingCardProps } from \"./components/listing-card.js\";\nexport { ActivityRow } from \"./components/activity-row.js\";\nexport type { ActivityRowProps } from \"./components/activity-row.js\";\nexport { ActivityFeedShell } from \"./components/activity-feed-shell.js\";\nexport type { ActivityFeedShellProps } from \"./components/activity-feed-shell.js\";\nexport { LaunchpadGrid } from \"./components/launchpad-grid.js\";\nexport type { LaunchpadGridProps, FeatureItem } from \"./components/launchpad-grid.js\";\nexport { CtaCardGrid } from \"./components/cta-card-grid.js\";\nexport type { CtaCardGridProps, CtaCardItem } from \"./components/cta-card-grid.js\";\n\n// ── v0.3.2 additions ─────────────────────────────────────────────────────────\nexport { DiscoverHero } from \"./components/discover-hero.js\";\nexport type { DiscoverHeroProps } from \"./components/discover-hero.js\";\nexport { FeaturedCarousel, FeaturedCarouselSkeleton } from \"./components/featured-carousel.js\";\nexport type { FeaturedCarouselProps } from \"./components/featured-carousel.js\";\nexport { DiscoverCollectionsStrip } from \"./components/discover-collections-strip.js\";\nexport type { DiscoverCollectionsStripProps } from \"./components/discover-collections-strip.js\";\nexport { DiscoverCreatorsStrip } from \"./components/discover-creators-strip.js\";\nexport type { DiscoverCreatorsStripProps } from \"./components/discover-creators-strip.js\";\nexport { DiscoverFeedSection } from \"./components/discover-feed-section.js\";\nexport type { DiscoverFeedSectionProps } from \"./components/discover-feed-section.js\";\n\n// ── v0.4 additions ────────────────────────────────────────────────────────────\nexport { LaunchpadServicesGrid } from \"./components/launchpad-services.js\";\nexport type { LaunchpadServicesGridProps, ServiceCardProps } from \"./components/launchpad-services.js\";\nexport { LAUNCHPAD_SERVICE_DEFINITIONS } from \"./data/launchpad-services.js\";\nexport type { ServiceDefinition, ServiceStatus, ServiceCategory } from \"./data/launchpad-services.js\";\n\n// ── v0.5.0 additions ─────────────────────────────────────────────────────────\nexport { NavCommandMenu, useNavCommandMenu } from \"./components/nav-command-menu.js\";\nexport type { NavCommand, NavCommandGroup, NavCommandMenuProps } from \"./components/nav-command-menu.js\";\n\n// ── v0.6.0 additions — portfolio subnav + counts ────────────────────────────\nexport { PortfolioSubnav } from \"./components/portfolio-subnav.js\";\nexport type {\n PortfolioSubnavProps,\n PortfolioNavItem,\n PortfolioNavGroup,\n PortfolioBadgeVariant,\n} from \"./components/portfolio-subnav.js\";\nexport { derivePortfolioCounts } from \"./utils/portfolio-counts.js\";\nexport type { PortfolioCounts, CountableOrder } from \"./utils/portfolio-counts.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,gBAAmB;AACnB,oBAAmC;AACnC,qBAA+B;AAC/B,kBAA2B;AAG3B,sBAA+C;AAE/C,mBAAsB;AAGtB,2BAA6C;AAG7C,2BAAyD;AAGzD,6BAA+B;AAG/B,wBAA8B;AAE9B,wBAAkC;AAIlC,+BAAyF;AACzF,4BAA8B;AAE9B,4BAA8B;AAE9B,0BAA4B;AAE5B,6BAAuD;AAEvD,wBAA6C;AAI7C,kBAAwB;AACxB,sBAAmD;AAEnD,yBAA+C;AAE/C,6BAA+B;AAE/B,0BAAiD;AAEjD,0BAA4B;AAE5B,iCAAkC;AAElC,4BAA8B;AAE9B,2BAA4B;AAI5B,2BAA6B;AAE7B,+BAA2D;AAE3D,wCAAyC;AAEzC,qCAAsC;AAEtC,mCAAoC;AAIpC,gCAAsC;AAEtC,IAAAA,6BAA8C;AAI9C,8BAAkD;AAIlD,8BAAgC;AAOhC,8BAAsC;","names":["import_launchpad_services"]}
package/dist/index.d.cts CHANGED
@@ -32,6 +32,8 @@ export { DiscoverFeedSection, DiscoverFeedSectionProps } from './components/disc
32
32
  export { LaunchpadServicesGrid, LaunchpadServicesGridProps, ServiceCardProps } from './components/launchpad-services.cjs';
33
33
  export { LAUNCHPAD_SERVICE_DEFINITIONS, ServiceCategory, ServiceDefinition, ServiceStatus } from './data/launchpad-services.cjs';
34
34
  export { NavCommand, NavCommandGroup, NavCommandMenu, NavCommandMenuProps, useNavCommandMenu } from './components/nav-command-menu.cjs';
35
+ export { PortfolioBadgeVariant, PortfolioNavGroup, PortfolioNavItem, PortfolioSubnav, PortfolioSubnavProps } from './components/portfolio-subnav.cjs';
36
+ export { CountableOrder, PortfolioCounts, derivePortfolioCounts } from './utils/portfolio-counts.cjs';
35
37
  import 'clsx';
36
38
  import 'react/jsx-runtime';
37
39
  import 'framer-motion';
package/dist/index.d.ts CHANGED
@@ -32,6 +32,8 @@ export { DiscoverFeedSection, DiscoverFeedSectionProps } from './components/disc
32
32
  export { LaunchpadServicesGrid, LaunchpadServicesGridProps, ServiceCardProps } from './components/launchpad-services.js';
33
33
  export { LAUNCHPAD_SERVICE_DEFINITIONS, ServiceCategory, ServiceDefinition, ServiceStatus } from './data/launchpad-services.js';
34
34
  export { NavCommand, NavCommandGroup, NavCommandMenu, NavCommandMenuProps, useNavCommandMenu } from './components/nav-command-menu.js';
35
+ export { PortfolioBadgeVariant, PortfolioNavGroup, PortfolioNavItem, PortfolioSubnav, PortfolioSubnavProps } from './components/portfolio-subnav.js';
36
+ export { CountableOrder, PortfolioCounts, derivePortfolioCounts } from './utils/portfolio-counts.js';
35
37
  import 'clsx';
36
38
  import 'react/jsx-runtime';
37
39
  import 'framer-motion';
package/dist/index.js CHANGED
@@ -32,6 +32,8 @@ import { DiscoverFeedSection } from "./components/discover-feed-section.js";
32
32
  import { LaunchpadServicesGrid } from "./components/launchpad-services.js";
33
33
  import { LAUNCHPAD_SERVICE_DEFINITIONS } from "./data/launchpad-services.js";
34
34
  import { NavCommandMenu, useNavCommandMenu } from "./components/nav-command-menu.js";
35
+ import { PortfolioSubnav } from "./components/portfolio-subnav.js";
36
+ import { derivePortfolioCounts } from "./utils/portfolio-counts.js";
35
37
  export {
36
38
  ACTIVITY_TYPE_CONFIG,
37
39
  ActivityFeedShell,
@@ -70,6 +72,7 @@ export {
70
72
  MotionCard,
71
73
  NavCommandMenu,
72
74
  PageContainer,
75
+ PortfolioSubnav,
73
76
  SPRING,
74
77
  ScrollSection,
75
78
  ShareButton,
@@ -79,6 +82,7 @@ export {
79
82
  TokenCard,
80
83
  TokenCardSkeleton,
81
84
  cn,
85
+ derivePortfolioCounts,
82
86
  formatDisplayPrice,
83
87
  ipfsToHttp,
84
88
  shortenAddress,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// ── Utils ─────────────────────────────────────────────────────────────────────\nexport { cn } from \"./utils/cn.js\";\nexport { formatDisplayPrice } from \"./utils/format.js\";\nexport { shortenAddress } from \"./utils/address.js\";\nexport { ipfsToHttp } from \"./utils/ipfs.js\";\n\n// ── Data (server-safe — no React, safe in Server Components) ──────────────────\nexport { IP_TYPE_DATA, IP_TYPE_DATA_MAP } from \"./data/ip-types.js\";\nexport type { IpTypeData } from \"./data/ip-types.js\";\nexport { BRAND } from \"./data/brand.js\";\n\n// ── Components (client-only — all have \"use client\") ─────────────────────────\nexport { CurrencyIcon, CurrencyAmount } from \"./components/currency-icon.js\";\nexport type { CurrencyIconProps, CurrencyAmountProps } from \"./components/currency-icon.js\";\n\nexport { IpTypeBadge, IP_TYPE_CONFIG, IP_TYPE_MAP } from \"./components/ip-type-badge.js\";\nexport type { IpTypeBadgeProps, IpTypeConfig } from \"./components/ip-type-badge.js\";\n\nexport { AddressDisplay } from \"./components/address-display.js\";\nexport type { AddressDisplayProps } from \"./components/address-display.js\";\n\nexport { MedialaneIcon } from \"./components/brand-icon.js\";\nexport type { MedialaneIconProps } from \"./components/brand-icon.js\";\nexport { MedialaneLogoFull } from \"./components/brand-logo.js\";\nexport type { MedialaneLogoFullProps } from \"./components/brand-logo.js\";\n\n// ── v0.2 additions ────────────────────────────────────────────────────────────\nexport { MotionCard, FadeIn, Stagger, StaggerItem, KineticWords, SPRING, EASE_OUT } from \"./components/motion-primitives.js\";\nexport { PageContainer } from \"./components/page-container.js\";\nexport type { PageContainerProps } from \"./components/page-container.js\";\nexport { ScrollSection } from \"./components/scroll-section.js\";\nexport type { ScrollSectionProps } from \"./components/scroll-section.js\";\nexport { ShareButton } from \"./components/share-button.js\";\nexport type { ShareButtonProps } from \"./components/share-button.js\";\nexport { CollectionCard, CollectionCardSkeleton } from \"./components/collection-card.js\";\nexport type { CollectionCardProps } from \"./components/collection-card.js\";\nexport { TokenCard, TokenCardSkeleton } from \"./components/token-card.js\";\nexport type { TokenCardProps, RarityTier } from \"./components/token-card.js\";\n\n// ── v0.3 additions ────────────────────────────────────────────────────────────\nexport { timeAgo } from \"./utils/time.js\";\nexport { ACTIVITY_TYPE_CONFIG, TYPE_FILTERS } from \"./data/activity.js\";\nexport type { ActivityTypeConfig } from \"./data/activity.js\";\nexport { HeroSlider, HeroSliderSkeleton } from \"./components/hero-slider.js\";\nexport type { HeroSliderProps } from \"./components/hero-slider.js\";\nexport { ActivityTicker } from \"./components/activity-ticker.js\";\nexport type { ActivityTickerProps } from \"./components/activity-ticker.js\";\nexport { ListingCard, ListingCardSkeleton } from \"./components/listing-card.js\";\nexport type { ListingCardProps } from \"./components/listing-card.js\";\nexport { ActivityRow } from \"./components/activity-row.js\";\nexport type { ActivityRowProps } from \"./components/activity-row.js\";\nexport { ActivityFeedShell } from \"./components/activity-feed-shell.js\";\nexport type { ActivityFeedShellProps } from \"./components/activity-feed-shell.js\";\nexport { LaunchpadGrid } from \"./components/launchpad-grid.js\";\nexport type { LaunchpadGridProps, FeatureItem } from \"./components/launchpad-grid.js\";\nexport { CtaCardGrid } from \"./components/cta-card-grid.js\";\nexport type { CtaCardGridProps, CtaCardItem } from \"./components/cta-card-grid.js\";\n\n// ── v0.3.2 additions ─────────────────────────────────────────────────────────\nexport { DiscoverHero } from \"./components/discover-hero.js\";\nexport type { DiscoverHeroProps } from \"./components/discover-hero.js\";\nexport { FeaturedCarousel, FeaturedCarouselSkeleton } from \"./components/featured-carousel.js\";\nexport type { FeaturedCarouselProps } from \"./components/featured-carousel.js\";\nexport { DiscoverCollectionsStrip } from \"./components/discover-collections-strip.js\";\nexport type { DiscoverCollectionsStripProps } from \"./components/discover-collections-strip.js\";\nexport { DiscoverCreatorsStrip } from \"./components/discover-creators-strip.js\";\nexport type { DiscoverCreatorsStripProps } from \"./components/discover-creators-strip.js\";\nexport { DiscoverFeedSection } from \"./components/discover-feed-section.js\";\nexport type { DiscoverFeedSectionProps } from \"./components/discover-feed-section.js\";\n\n// ── v0.4 additions ────────────────────────────────────────────────────────────\nexport { LaunchpadServicesGrid } from \"./components/launchpad-services.js\";\nexport type { LaunchpadServicesGridProps, ServiceCardProps } from \"./components/launchpad-services.js\";\nexport { LAUNCHPAD_SERVICE_DEFINITIONS } from \"./data/launchpad-services.js\";\nexport type { ServiceDefinition, ServiceStatus, ServiceCategory } from \"./data/launchpad-services.js\";\n\n// ── v0.5.0 additions ─────────────────────────────────────────────────────────\nexport { NavCommandMenu, useNavCommandMenu } from \"./components/nav-command-menu.js\";\nexport type { NavCommand, NavCommandGroup, NavCommandMenuProps } from \"./components/nav-command-menu.js\";\n"],"mappings":"AACA,SAAS,UAAU;AACnB,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAG3B,SAAS,cAAc,wBAAwB;AAE/C,SAAS,aAAa;AAGtB,SAAS,cAAc,sBAAsB;AAG7C,SAAS,aAAa,gBAAgB,mBAAmB;AAGzD,SAAS,sBAAsB;AAG/B,SAAS,qBAAqB;AAE9B,SAAS,yBAAyB;AAIlC,SAAS,YAAY,QAAQ,SAAS,aAAa,cAAc,QAAQ,gBAAgB;AACzF,SAAS,qBAAqB;AAE9B,SAAS,qBAAqB;AAE9B,SAAS,mBAAmB;AAE5B,SAAS,gBAAgB,8BAA8B;AAEvD,SAAS,WAAW,yBAAyB;AAI7C,SAAS,eAAe;AACxB,SAAS,sBAAsB,oBAAoB;AAEnD,SAAS,YAAY,0BAA0B;AAE/C,SAAS,sBAAsB;AAE/B,SAAS,aAAa,2BAA2B;AAEjD,SAAS,mBAAmB;AAE5B,SAAS,yBAAyB;AAElC,SAAS,qBAAqB;AAE9B,SAAS,mBAAmB;AAI5B,SAAS,oBAAoB;AAE7B,SAAS,kBAAkB,gCAAgC;AAE3D,SAAS,gCAAgC;AAEzC,SAAS,6BAA6B;AAEtC,SAAS,2BAA2B;AAIpC,SAAS,6BAA6B;AAEtC,SAAS,qCAAqC;AAI9C,SAAS,gBAAgB,yBAAyB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// ── Utils ─────────────────────────────────────────────────────────────────────\nexport { cn } from \"./utils/cn.js\";\nexport { formatDisplayPrice } from \"./utils/format.js\";\nexport { shortenAddress } from \"./utils/address.js\";\nexport { ipfsToHttp } from \"./utils/ipfs.js\";\n\n// ── Data (server-safe — no React, safe in Server Components) ──────────────────\nexport { IP_TYPE_DATA, IP_TYPE_DATA_MAP } from \"./data/ip-types.js\";\nexport type { IpTypeData } from \"./data/ip-types.js\";\nexport { BRAND } from \"./data/brand.js\";\n\n// ── Components (client-only — all have \"use client\") ─────────────────────────\nexport { CurrencyIcon, CurrencyAmount } from \"./components/currency-icon.js\";\nexport type { CurrencyIconProps, CurrencyAmountProps } from \"./components/currency-icon.js\";\n\nexport { IpTypeBadge, IP_TYPE_CONFIG, IP_TYPE_MAP } from \"./components/ip-type-badge.js\";\nexport type { IpTypeBadgeProps, IpTypeConfig } from \"./components/ip-type-badge.js\";\n\nexport { AddressDisplay } from \"./components/address-display.js\";\nexport type { AddressDisplayProps } from \"./components/address-display.js\";\n\nexport { MedialaneIcon } from \"./components/brand-icon.js\";\nexport type { MedialaneIconProps } from \"./components/brand-icon.js\";\nexport { MedialaneLogoFull } from \"./components/brand-logo.js\";\nexport type { MedialaneLogoFullProps } from \"./components/brand-logo.js\";\n\n// ── v0.2 additions ────────────────────────────────────────────────────────────\nexport { MotionCard, FadeIn, Stagger, StaggerItem, KineticWords, SPRING, EASE_OUT } from \"./components/motion-primitives.js\";\nexport { PageContainer } from \"./components/page-container.js\";\nexport type { PageContainerProps } from \"./components/page-container.js\";\nexport { ScrollSection } from \"./components/scroll-section.js\";\nexport type { ScrollSectionProps } from \"./components/scroll-section.js\";\nexport { ShareButton } from \"./components/share-button.js\";\nexport type { ShareButtonProps } from \"./components/share-button.js\";\nexport { CollectionCard, CollectionCardSkeleton } from \"./components/collection-card.js\";\nexport type { CollectionCardProps } from \"./components/collection-card.js\";\nexport { TokenCard, TokenCardSkeleton } from \"./components/token-card.js\";\nexport type { TokenCardProps, RarityTier } from \"./components/token-card.js\";\n\n// ── v0.3 additions ────────────────────────────────────────────────────────────\nexport { timeAgo } from \"./utils/time.js\";\nexport { ACTIVITY_TYPE_CONFIG, TYPE_FILTERS } from \"./data/activity.js\";\nexport type { ActivityTypeConfig } from \"./data/activity.js\";\nexport { HeroSlider, HeroSliderSkeleton } from \"./components/hero-slider.js\";\nexport type { HeroSliderProps } from \"./components/hero-slider.js\";\nexport { ActivityTicker } from \"./components/activity-ticker.js\";\nexport type { ActivityTickerProps } from \"./components/activity-ticker.js\";\nexport { ListingCard, ListingCardSkeleton } from \"./components/listing-card.js\";\nexport type { ListingCardProps } from \"./components/listing-card.js\";\nexport { ActivityRow } from \"./components/activity-row.js\";\nexport type { ActivityRowProps } from \"./components/activity-row.js\";\nexport { ActivityFeedShell } from \"./components/activity-feed-shell.js\";\nexport type { ActivityFeedShellProps } from \"./components/activity-feed-shell.js\";\nexport { LaunchpadGrid } from \"./components/launchpad-grid.js\";\nexport type { LaunchpadGridProps, FeatureItem } from \"./components/launchpad-grid.js\";\nexport { CtaCardGrid } from \"./components/cta-card-grid.js\";\nexport type { CtaCardGridProps, CtaCardItem } from \"./components/cta-card-grid.js\";\n\n// ── v0.3.2 additions ─────────────────────────────────────────────────────────\nexport { DiscoverHero } from \"./components/discover-hero.js\";\nexport type { DiscoverHeroProps } from \"./components/discover-hero.js\";\nexport { FeaturedCarousel, FeaturedCarouselSkeleton } from \"./components/featured-carousel.js\";\nexport type { FeaturedCarouselProps } from \"./components/featured-carousel.js\";\nexport { DiscoverCollectionsStrip } from \"./components/discover-collections-strip.js\";\nexport type { DiscoverCollectionsStripProps } from \"./components/discover-collections-strip.js\";\nexport { DiscoverCreatorsStrip } from \"./components/discover-creators-strip.js\";\nexport type { DiscoverCreatorsStripProps } from \"./components/discover-creators-strip.js\";\nexport { DiscoverFeedSection } from \"./components/discover-feed-section.js\";\nexport type { DiscoverFeedSectionProps } from \"./components/discover-feed-section.js\";\n\n// ── v0.4 additions ────────────────────────────────────────────────────────────\nexport { LaunchpadServicesGrid } from \"./components/launchpad-services.js\";\nexport type { LaunchpadServicesGridProps, ServiceCardProps } from \"./components/launchpad-services.js\";\nexport { LAUNCHPAD_SERVICE_DEFINITIONS } from \"./data/launchpad-services.js\";\nexport type { ServiceDefinition, ServiceStatus, ServiceCategory } from \"./data/launchpad-services.js\";\n\n// ── v0.5.0 additions ─────────────────────────────────────────────────────────\nexport { NavCommandMenu, useNavCommandMenu } from \"./components/nav-command-menu.js\";\nexport type { NavCommand, NavCommandGroup, NavCommandMenuProps } from \"./components/nav-command-menu.js\";\n\n// ── v0.6.0 additions — portfolio subnav + counts ────────────────────────────\nexport { PortfolioSubnav } from \"./components/portfolio-subnav.js\";\nexport type {\n PortfolioSubnavProps,\n PortfolioNavItem,\n PortfolioNavGroup,\n PortfolioBadgeVariant,\n} from \"./components/portfolio-subnav.js\";\nexport { derivePortfolioCounts } from \"./utils/portfolio-counts.js\";\nexport type { PortfolioCounts, CountableOrder } from \"./utils/portfolio-counts.js\";\n"],"mappings":"AACA,SAAS,UAAU;AACnB,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAG3B,SAAS,cAAc,wBAAwB;AAE/C,SAAS,aAAa;AAGtB,SAAS,cAAc,sBAAsB;AAG7C,SAAS,aAAa,gBAAgB,mBAAmB;AAGzD,SAAS,sBAAsB;AAG/B,SAAS,qBAAqB;AAE9B,SAAS,yBAAyB;AAIlC,SAAS,YAAY,QAAQ,SAAS,aAAa,cAAc,QAAQ,gBAAgB;AACzF,SAAS,qBAAqB;AAE9B,SAAS,qBAAqB;AAE9B,SAAS,mBAAmB;AAE5B,SAAS,gBAAgB,8BAA8B;AAEvD,SAAS,WAAW,yBAAyB;AAI7C,SAAS,eAAe;AACxB,SAAS,sBAAsB,oBAAoB;AAEnD,SAAS,YAAY,0BAA0B;AAE/C,SAAS,sBAAsB;AAE/B,SAAS,aAAa,2BAA2B;AAEjD,SAAS,mBAAmB;AAE5B,SAAS,yBAAyB;AAElC,SAAS,qBAAqB;AAE9B,SAAS,mBAAmB;AAI5B,SAAS,oBAAoB;AAE7B,SAAS,kBAAkB,gCAAgC;AAE3D,SAAS,gCAAgC;AAEzC,SAAS,6BAA6B;AAEtC,SAAS,2BAA2B;AAIpC,SAAS,6BAA6B;AAEtC,SAAS,qCAAqC;AAI9C,SAAS,gBAAgB,yBAAyB;AAIlD,SAAS,uBAAuB;AAOhC,SAAS,6BAA6B;","names":[]}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var portfolio_counts_exports = {};
20
+ __export(portfolio_counts_exports, {
21
+ derivePortfolioCounts: () => derivePortfolioCounts
22
+ });
23
+ module.exports = __toCommonJS(portfolio_counts_exports);
24
+ function derivePortfolioCounts(orders, remixOffers, address) {
25
+ const list = Array.isArray(orders) ? orders : [];
26
+ const addr = (address ?? "").toLowerCase();
27
+ const received = list.filter(
28
+ (o) => o.status === "ACTIVE" && o.offer.itemType === "ERC20" && o.offerer.toLowerCase() !== addr
29
+ ).length;
30
+ const listings = list.filter(
31
+ (o) => (o.offer.itemType === "ERC721" || o.offer.itemType === "ERC1155") && o.status === "ACTIVE"
32
+ ).length;
33
+ const remix = Array.isArray(remixOffers) ? remixOffers.filter(
34
+ (o) => o.status === "PENDING" || o.status === "AUTO_PENDING"
35
+ ).length : 0;
36
+ const counter = list.filter(
37
+ (o) => o.offer.itemType === "ERC20" && o.offerer.toLowerCase() === addr && o.hasActiveCounterOffer === true
38
+ ).length;
39
+ return { received, listings, remix, counter };
40
+ }
41
+ // Annotate the CommonJS export names for ESM import in node:
42
+ 0 && (module.exports = {
43
+ derivePortfolioCounts
44
+ });
45
+ //# sourceMappingURL=portfolio-counts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/portfolio-counts.ts"],"sourcesContent":["/**\n * Minimal structural shape of a marketplace order needed to derive portfolio\n * counts. `ApiOrder` from `@medialane/sdk` is structurally assignable to this,\n * so callers pass their `ApiOrder[]` directly and the package stays decoupled\n * from any specific SDK version.\n */\nexport interface CountableOrder {\n status: string;\n offerer: string;\n offer: { itemType: string };\n hasActiveCounterOffer?: boolean;\n}\n\nexport interface PortfolioCounts {\n /** Active ERC-20 offers made by others on the user's assets. */\n received: number;\n /** Active ERC-721/ERC-1155 listings the user is selling. */\n listings: number;\n /** Pending remix offers awaiting the user's action. */\n remix: number;\n /** The user's bids that a seller has countered. */\n counter: number;\n}\n\n/**\n * Derive the portfolio header/subnav counts from the user's orders and remix\n * offers. Pure and server-safe (no React). Guards nullish inputs.\n */\nexport function derivePortfolioCounts(\n orders: ReadonlyArray<CountableOrder> | null | undefined,\n remixOffers: ReadonlyArray<{ status: string }> | null | undefined,\n address: string | null | undefined,\n): PortfolioCounts {\n const list = Array.isArray(orders) ? orders : [];\n const addr = (address ?? \"\").toLowerCase();\n\n const received = list.filter(\n (o) =>\n o.status === \"ACTIVE\" &&\n o.offer.itemType === \"ERC20\" &&\n o.offerer.toLowerCase() !== addr,\n ).length;\n\n const listings = list.filter(\n (o) =>\n (o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\") &&\n o.status === \"ACTIVE\",\n ).length;\n\n const remix = Array.isArray(remixOffers)\n ? remixOffers.filter(\n (o) => o.status === \"PENDING\" || o.status === \"AUTO_PENDING\",\n ).length\n : 0;\n\n const counter = list.filter(\n (o) =>\n o.offer.itemType === \"ERC20\" &&\n o.offerer.toLowerCase() === addr &&\n o.hasActiveCounterOffer === true,\n ).length;\n\n return { received, listings, remix, counter };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BO,SAAS,sBACd,QACA,aACA,SACiB;AACjB,QAAM,OAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;AAC/C,QAAM,QAAQ,WAAW,IAAI,YAAY;AAEzC,QAAM,WAAW,KAAK;AAAA,IACpB,CAAC,MACC,EAAE,WAAW,YACb,EAAE,MAAM,aAAa,WACrB,EAAE,QAAQ,YAAY,MAAM;AAAA,EAChC,EAAE;AAEF,QAAM,WAAW,KAAK;AAAA,IACpB,CAAC,OACE,EAAE,MAAM,aAAa,YAAY,EAAE,MAAM,aAAa,cACvD,EAAE,WAAW;AAAA,EACjB,EAAE;AAEF,QAAM,QAAQ,MAAM,QAAQ,WAAW,IACnC,YAAY;AAAA,IACV,CAAC,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW;AAAA,EAChD,EAAE,SACF;AAEJ,QAAM,UAAU,KAAK;AAAA,IACnB,CAAC,MACC,EAAE,MAAM,aAAa,WACrB,EAAE,QAAQ,YAAY,MAAM,QAC5B,EAAE,0BAA0B;AAAA,EAChC,EAAE;AAEF,SAAO,EAAE,UAAU,UAAU,OAAO,QAAQ;AAC9C;","names":[]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Minimal structural shape of a marketplace order needed to derive portfolio
3
+ * counts. `ApiOrder` from `@medialane/sdk` is structurally assignable to this,
4
+ * so callers pass their `ApiOrder[]` directly and the package stays decoupled
5
+ * from any specific SDK version.
6
+ */
7
+ interface CountableOrder {
8
+ status: string;
9
+ offerer: string;
10
+ offer: {
11
+ itemType: string;
12
+ };
13
+ hasActiveCounterOffer?: boolean;
14
+ }
15
+ interface PortfolioCounts {
16
+ /** Active ERC-20 offers made by others on the user's assets. */
17
+ received: number;
18
+ /** Active ERC-721/ERC-1155 listings the user is selling. */
19
+ listings: number;
20
+ /** Pending remix offers awaiting the user's action. */
21
+ remix: number;
22
+ /** The user's bids that a seller has countered. */
23
+ counter: number;
24
+ }
25
+ /**
26
+ * Derive the portfolio header/subnav counts from the user's orders and remix
27
+ * offers. Pure and server-safe (no React). Guards nullish inputs.
28
+ */
29
+ declare function derivePortfolioCounts(orders: ReadonlyArray<CountableOrder> | null | undefined, remixOffers: ReadonlyArray<{
30
+ status: string;
31
+ }> | null | undefined, address: string | null | undefined): PortfolioCounts;
32
+
33
+ export { type CountableOrder, type PortfolioCounts, derivePortfolioCounts };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Minimal structural shape of a marketplace order needed to derive portfolio
3
+ * counts. `ApiOrder` from `@medialane/sdk` is structurally assignable to this,
4
+ * so callers pass their `ApiOrder[]` directly and the package stays decoupled
5
+ * from any specific SDK version.
6
+ */
7
+ interface CountableOrder {
8
+ status: string;
9
+ offerer: string;
10
+ offer: {
11
+ itemType: string;
12
+ };
13
+ hasActiveCounterOffer?: boolean;
14
+ }
15
+ interface PortfolioCounts {
16
+ /** Active ERC-20 offers made by others on the user's assets. */
17
+ received: number;
18
+ /** Active ERC-721/ERC-1155 listings the user is selling. */
19
+ listings: number;
20
+ /** Pending remix offers awaiting the user's action. */
21
+ remix: number;
22
+ /** The user's bids that a seller has countered. */
23
+ counter: number;
24
+ }
25
+ /**
26
+ * Derive the portfolio header/subnav counts from the user's orders and remix
27
+ * offers. Pure and server-safe (no React). Guards nullish inputs.
28
+ */
29
+ declare function derivePortfolioCounts(orders: ReadonlyArray<CountableOrder> | null | undefined, remixOffers: ReadonlyArray<{
30
+ status: string;
31
+ }> | null | undefined, address: string | null | undefined): PortfolioCounts;
32
+
33
+ export { type CountableOrder, type PortfolioCounts, derivePortfolioCounts };
@@ -0,0 +1,21 @@
1
+ function derivePortfolioCounts(orders, remixOffers, address) {
2
+ const list = Array.isArray(orders) ? orders : [];
3
+ const addr = (address ?? "").toLowerCase();
4
+ const received = list.filter(
5
+ (o) => o.status === "ACTIVE" && o.offer.itemType === "ERC20" && o.offerer.toLowerCase() !== addr
6
+ ).length;
7
+ const listings = list.filter(
8
+ (o) => (o.offer.itemType === "ERC721" || o.offer.itemType === "ERC1155") && o.status === "ACTIVE"
9
+ ).length;
10
+ const remix = Array.isArray(remixOffers) ? remixOffers.filter(
11
+ (o) => o.status === "PENDING" || o.status === "AUTO_PENDING"
12
+ ).length : 0;
13
+ const counter = list.filter(
14
+ (o) => o.offer.itemType === "ERC20" && o.offerer.toLowerCase() === addr && o.hasActiveCounterOffer === true
15
+ ).length;
16
+ return { received, listings, remix, counter };
17
+ }
18
+ export {
19
+ derivePortfolioCounts
20
+ };
21
+ //# sourceMappingURL=portfolio-counts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/portfolio-counts.ts"],"sourcesContent":["/**\n * Minimal structural shape of a marketplace order needed to derive portfolio\n * counts. `ApiOrder` from `@medialane/sdk` is structurally assignable to this,\n * so callers pass their `ApiOrder[]` directly and the package stays decoupled\n * from any specific SDK version.\n */\nexport interface CountableOrder {\n status: string;\n offerer: string;\n offer: { itemType: string };\n hasActiveCounterOffer?: boolean;\n}\n\nexport interface PortfolioCounts {\n /** Active ERC-20 offers made by others on the user's assets. */\n received: number;\n /** Active ERC-721/ERC-1155 listings the user is selling. */\n listings: number;\n /** Pending remix offers awaiting the user's action. */\n remix: number;\n /** The user's bids that a seller has countered. */\n counter: number;\n}\n\n/**\n * Derive the portfolio header/subnav counts from the user's orders and remix\n * offers. Pure and server-safe (no React). Guards nullish inputs.\n */\nexport function derivePortfolioCounts(\n orders: ReadonlyArray<CountableOrder> | null | undefined,\n remixOffers: ReadonlyArray<{ status: string }> | null | undefined,\n address: string | null | undefined,\n): PortfolioCounts {\n const list = Array.isArray(orders) ? orders : [];\n const addr = (address ?? \"\").toLowerCase();\n\n const received = list.filter(\n (o) =>\n o.status === \"ACTIVE\" &&\n o.offer.itemType === \"ERC20\" &&\n o.offerer.toLowerCase() !== addr,\n ).length;\n\n const listings = list.filter(\n (o) =>\n (o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\") &&\n o.status === \"ACTIVE\",\n ).length;\n\n const remix = Array.isArray(remixOffers)\n ? remixOffers.filter(\n (o) => o.status === \"PENDING\" || o.status === \"AUTO_PENDING\",\n ).length\n : 0;\n\n const counter = list.filter(\n (o) =>\n o.offer.itemType === \"ERC20\" &&\n o.offerer.toLowerCase() === addr &&\n o.hasActiveCounterOffer === true,\n ).length;\n\n return { received, listings, remix, counter };\n}\n"],"mappings":"AA4BO,SAAS,sBACd,QACA,aACA,SACiB;AACjB,QAAM,OAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;AAC/C,QAAM,QAAQ,WAAW,IAAI,YAAY;AAEzC,QAAM,WAAW,KAAK;AAAA,IACpB,CAAC,MACC,EAAE,WAAW,YACb,EAAE,MAAM,aAAa,WACrB,EAAE,QAAQ,YAAY,MAAM;AAAA,EAChC,EAAE;AAEF,QAAM,WAAW,KAAK;AAAA,IACpB,CAAC,OACE,EAAE,MAAM,aAAa,YAAY,EAAE,MAAM,aAAa,cACvD,EAAE,WAAW;AAAA,EACjB,EAAE;AAEF,QAAM,QAAQ,MAAM,QAAQ,WAAW,IACnC,YAAY;AAAA,IACV,CAAC,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW;AAAA,EAChD,EAAE,SACF;AAEJ,QAAM,UAAU,KAAK;AAAA,IACnB,CAAC,MACC,EAAE,MAAM,aAAa,WACrB,EAAE,QAAQ,YAAY,MAAM,QAC5B,EAAE,0BAA0B;AAAA,EAChC,EAAE;AAEF,SAAO,EAAE,UAAU,UAAU,OAAO,QAAQ;AAC9C;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medialane/ui",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Shared UI components for Medialane apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",