"use client"; // The standard path, and the only part of the bar a first-time visitor has to // understand: a query box, a Search button, and one row of chips for everything // else. Below lg it sticks under the header, so the search box is always one // glance away no matter how far down the results the reader has gone. // // Everything that used to sit ABOVE this — the profile row, five filter // sections, the availability tree, the date pickers — now lives in FiltersPanel // behind the Filters chip (or inline from xl). That stack was two phone screens // tall and it sat between the visitor and the thing they came for. import { useMemo, type ReactNode } from "react"; import { LayersIcon, SearchIcon, Share2Icon } from "lucide-react"; import { isCompactRoot } from "../lib/searchQuery"; import { MISSING_STATES } from "../lib/availability"; import { Button } from "./ui/button"; import QueryBuilder, { compactLayerActions } from "./QueryBuilder"; import { FiltersTrigger } from "./FiltersContainer"; import { useMediaQuery } from "../lib/useMediaQuery"; import { SEARCH_IN_REFUSAL, useSearchSession } from "./SearchSessionContext"; export default function SearchBar({ nav }: { nav?: ReactNode }) { const { commitSearch, mounted, draftRoot, setDraftRoot, leafStates, groupStates, queryDirty, filtersDirty, searchedThisPageLife, handleResetLayers, layersResetDisabled, hasSubs, liveChatTotalCount, shareCopied, handleShareCurrentSearch, draftExcludedChannels, draftNov, draftNol, draftNop, draftNotr, draftLc, draftSearchInEmpty, draftNaa, draftNar, draftStates, draftDateFrom, draftDateTo, draftTags, } = useSearchSession(); // From xl the filters are an inline block under the bar with their own // disclosure, so the chip (and the bar's share button, which the profile row // already carries) would be a second copy of a control that is on screen. const inlineFilters = useMediaQuery("(min-width: 1280px)"); const compact = isCompactRoot(draftRoot); const layers = compactLayerActions(draftRoot, setDraftRoot); // What the Filters chip's badge counts: anything the reader has narrowed — // and the "Search in" row off its default, which can widen (Live chat) as // well as narrow, because either way a plain query no longer reads what it // reads by default and the panel that says so may be behind the chip. const activeFilters = useMemo(() => { let n = 0; if (draftExcludedChannels.size > 0) n += 1; if (draftNotr || draftNop || draftLc) n += 1; if (draftNov || draftNol) n += 1; if (draftNaa || draftNar) n += 1; if (draftStates.size < MISSING_STATES.length + 1) n += 1; if (draftDateFrom || draftDateTo) n += 1; if (draftTags.size > 0) n += 1; return n; }, [ draftExcludedChannels, draftNov, draftNol, draftNop, draftNotr, draftLc, draftNaa, draftNar, draftStates, draftDateFrom, draftDateTo, draftTags, ]); // The line under the bar says what to do: after an edit that is not applied // yet, and before the first Search of the page life, when the results area // is empty and waits for it. Not while Search is refused ("Search in" has // nothing ticked) — the panel's own line says what to fix. const promptApply = !draftSearchInEmpty && (queryDirty || filtersDirty || !searchedThisPageLife); // The hint points at the row, so it goes once the box is ticked. const chatHint = hasSubs && !draftLc; const submit = ( ); const resetLayers = ( ); return ( // A fragment, not a wrapper: `position: sticky` only travels inside its // containing block, so the form has to be a DIRECT child of the tall column // that spans the bar and the results (SiteWorkspace / TranscriptSearch). // Wrapped in a div of its own it stuck for its own height and no further. <>
{/* One status line under the bar instead of two hints competing for room inside it — and a SIBLING of the form, so the pinned block is never more than the input row plus the chips. */} {(promptApply || chatHint) && ({/* The site's accent, not the warning hue: an unapplied edit is the next step, not a fault. `--brand` clears 4.5:1 on every base's page ground (lib/brand.ts MIN_ACCENT_CONTRAST). */} {promptApply && ( Press Enter or click Search to apply )} {chatHint && ( Live chat available on {liveChatTotalCount} video {liveChatTotalCount === 1 ? "" : "s"} — tick Live chat under Search in. )}
)} > ); }