SPB Git

spb/rareindex

Public
4.1 KB · 110 lines tsx
1'use client';23import { useRouter } from 'next/navigation';4import { useEffect, useState } from 'react';56interface Suggestion {7  type: string;8  label: string;9  sublabel: string | null;10  href: string;11}1213const SLOTS = ['a', 'b', 'c', 'd'] as const;1415/** Four slot pickers with suggestions; values are asset slugs or index tickers, stored in the URL. */16export function CompareForm({ initial }: { initial: string[] }) {17  const router = useRouter();18  const [values, setValues] = useState<string[]>([...initial, '', '', '', ''].slice(0, 4));19  const [typing, setTyping] = useState<{ i: number; q: string } | null>(null);20  const [items, setItems] = useState<Suggestion[]>([]);2122  useEffect(() => {23    if (!typing || typing.q.trim().length < 2) return;24    const ctrl = new AbortController();25    const t = setTimeout(() => {26      fetch(`/api/search/suggest?q=${encodeURIComponent(typing.q)}`, { signal: ctrl.signal })27        .then((r) => r.json())28        .then((d: { items: Suggestion[] }) => setItems((d.items ?? []).filter((x) => x.type === 'asset' || x.type === 'index')))29        .catch(() => {});30    }, 150);31    return () => {32      clearTimeout(t);33      ctrl.abort();34    };35  }, [typing]);3637  const apply = (vals: string[]) => {38    const sp = new URLSearchParams();39    vals.forEach((v, i) => {40      if (v) sp.set(SLOTS[i]!, v);41    });42    router.push(`/compare?${sp.toString()}`);43  };4445  return (46    <form47      className="card grid gap-2 p-3 sm:grid-cols-4"48      onSubmit={(e) => {49        e.preventDefault();50        apply(values);51      }}52    >53      {SLOTS.map((s, i) => (54        <label key={s} className="relative flex flex-col gap-0.5 text-[10px] font-medium uppercase tracking-wider text-subtle">55          Slot {s.toUpperCase()}56          <input57            value={values[i] ?? ''}58            onChange={(e) => {59              const v = [...values];60              v[i] = e.target.value;61              setValues(v);62              setTyping({ i, q: e.target.value });63              if (e.target.value.trim().length < 2) setItems([]);64            }}65            onBlur={() =>66              setTimeout(() => {67                setTyping(null);68                setItems([]);69              }, 150)70            }71            placeholder="asset slug or RARE-TCG"72            className="h-9 rounded-md border border-border bg-elevated px-2 text-[13px] normal-case tracking-normal text-fg placeholder:text-subtle focus:border-border-strong focus:outline-none"73          />74          {typing?.i === i && items.length ? (75            <ul className="absolute left-0 top-full z-20 mt-1 w-full overflow-hidden rounded-md border border-border bg-elevated text-[12px] normal-case tracking-normal shadow-pop">76              {items.map((it) => {77                const val = it.href.startsWith('/asset/') ? it.href.slice(7) : it.href.startsWith('/rareindex/') ? it.href.slice(11) : it.label;78                return (79                  <li key={it.href}>80                    <button81                      type="button"82                      onMouseDown={(e) => e.preventDefault()}83                      onClick={() => {84                        const v = [...values];85                        v[i] = val;86                        setValues(v);87                        setTyping(null);88                        apply(v);89                      }}90                      className="block w-full truncate px-2 py-1.5 text-left text-fg hover:bg-sunken"91                    >92                      {it.label} <span className="text-subtle">· {it.sublabel}</span>93                    </button>94                  </li>95                );96              })}97            </ul>98          ) : null}99        </label>100      ))}101      <div className="sm:col-span-4 flex items-center justify-between text-[11px] text-subtle">102        <span>Tip: paste slugs from asset URLs; tickers: RARE, RARE-TCG, RARE-SPORT, RARE-WATCH, RARE-SNEAKER, RARE-LEGO, RARE-COMIC, RARE-GAME…</span>103        <button type="submit" className="rounded-md bg-accent px-3 py-1.5 text-xs font-medium text-accent-fg">104          Compare105        </button>106      </div>107    </form>108  );109}110
Files · d92b8b3d34ca4c272753d94b3a3ee1ff6e902121
Menu

Theme