Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/components/ui/input/InputSearchWithURL.tsx
T

69 lines
2.4 KiB
TypeScript

import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useSearchParams } from "react-router";
import ButtonPrimary from "../buttons/ButtonPrimary";
import clsx from "clsx";
import { TextInput } from "~/ui/components/TextInput";
interface Props {
onNew?: () => void;
onNewRoute?: string;
placeholder?: string;
size?: "xs" | "sm" | "lg";
className?: string;
}
// Subframe migration (strangler shim): renders Subframe TextInput with type="search"
// (built-in search icon) behind the original API. The debounced URL-sync logic is
// unchanged. Subframe's own SearchInput is presentational only and cannot back a
// controlled field. See docs/subframe/COMPONENT-MAPPING.md.
export default function InputSearchWithURL({ onNew, onNewRoute, placeholder, size = "lg", className }: Props) {
const [searchParams, setSearchParams] = useSearchParams();
const { t } = useTranslation();
const [search, setSearch] = useState(searchParams.get("q")?.toString() ?? "");
const [debouncedSearch, setDebouncedSearch] = useState(searchParams.get("q")?.toString() ?? "");
useEffect(() => {
const timer = setTimeout(() => setDebouncedSearch(search), 500);
return () => clearTimeout(timer);
}, [search]);
useEffect(() => {
const query = searchParams.get("q") ?? "";
if (query !== debouncedSearch) {
searchParams.set("q", debouncedSearch);
if (searchParams.get("page")) {
searchParams.set("page", "1");
}
if (searchParams.get("pageSize")) {
// searchParams.delete("pageSize");
}
setSearchParams(searchParams, {
preventScrollReset: true,
});
}
}, [debouncedSearch, searchParams, setSearchParams]);
const resolvedPlaceholder = placeholder ?? t("shared.searchDot");
return (
<div className={clsx("flex justify-between space-x-2", className)}>
<TextInput className="w-full max-w-none" type="search" size={size === "lg" ? "medium" : "small"}>
<TextInput.Input
type="search"
name="q"
aria-label={resolvedPlaceholder}
placeholder={resolvedPlaceholder}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
autoComplete="off"
/>
</TextInput>
{onNew && <ButtonPrimary onClick={onNew}>{t("shared.new")}</ButtonPrimary>}
{onNewRoute && <ButtonPrimary to={onNewRoute}>{t("shared.new")}</ButtonPrimary>}
</div>
);
}