Untitled
unknown
plain_text
a year ago
9.5 kB
5
Indexable
"use client";
import React, { useEffect, useState, useCallback } from "react";
import Image from "next/image";
import { demoResourceNames } from "@/fd-toolbox/resources/demo-resource-names";
import { FdSearchResults } from "./FdSearchResult";
import { WithIndexer } from "@/fd-toolbox/types/with-indexer";
import { WithId } from "@/fd-toolbox/types/with-id";
import { FdIcon } from "@/fd-component-toolbox/icon/FdIcon";
import { FdSeparator } from "@/fd-component-toolbox/separator/FdSeparator";
import { FdBadge } from "@/fd-component-toolbox/badge/FdBadge";
import debounce from "lodash.debounce";
import { tableDisplayModes } from "../../fd-toolbox/enums/enums";
import { notFoundImg } from "./FdSearchDialog.funcs";
interface FdSearchDialogProps {
searchText: string;
onSearchCallback: (event: React.ChangeEvent<HTMLInputElement>) => void;
onInputFocus: () => void;
inputRef: React.RefObject<HTMLInputElement>;
hasInputText: boolean;
}
export function FdSearchDialog({
searchText,
onSearchCallback,
onInputFocus,
inputRef,
hasInputText,
}: FdSearchDialogProps) {
const [isOpenSearchWindow, setIsOpenSearchWindow] = useState(false);
const [searchTextState, setSearchTextState] = useState(searchText);
const [selectedResourceNames, setSelectedResourceNames] = useState<string[]>([]);
const [searchResults, setSearchResults] = useState<WithIndexer<WithId[]>>({});
const allSelected = selectedResourceNames.length === Object.values(demoResourceNames).length;
const openSearchWindow = useCallback(() => setIsOpenSearchWindow(true), []);
const debouncedSearch = debounce((value: string) => {
const fakeEvent = { target: { value } } as React.ChangeEvent<HTMLInputElement>;
onSearchCallback(fakeEvent);
}, 300);
useEffect(() => {
setSearchTextState(searchText);
}, [searchText]);
useEffect(() => {
return () => debouncedSearch.cancel();
}, [debouncedSearch]);
useEffect(() => {
setSelectedResourceNames(Object.values(demoResourceNames));
}, []);
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
setSearchTextState(value);
debouncedSearch(value);
};
const toggleResource = (resource: string) => {
setSelectedResourceNames((prev) => {
if (allSelected) return [resource];
return prev.includes(resource) ? prev.filter((item) => item !== resource) : [...prev, resource];
});
if (allSelected) setSelectedResourceNames([resource]);
};
const toggleAllResources = () => {
if (allSelected) return;
if (selectedResourceNames.length === Object.values(demoResourceNames).length)
setSelectedResourceNames([]);
else setSelectedResourceNames(Object.values(demoResourceNames));
};
return (
<>
<input
id="toolbar-search-input"
type="text"
value={searchTextState}
ref={inputRef}
placeholder="Search"
className={`peer flex w-full rounded-md border-none px-10 py-1.5 text-sm outline-none focus:border-none focus:bg-popover focus:text-popover-foreground ${
hasInputText
? "bg-popover text-popover-foreground"
: "bg-fd-toolbar-hover placeholder:text-fd-sidebar-icons focus:placeholder:text-fd-toolbar-input"
}`}
onChange={handleInputChange}
onFocus={onInputFocus}
onClick={openSearchWindow}
/>
{isOpenSearchWindow && (
<div className="fixed inset-0 z-20 mt-11 flex items-center justify-center">
<div
className="absolute inset-0"
onClick={() => {
setIsOpenSearchWindow(false);
setSearchTextState("");
}}
/>
<div className="relative z-10 ml-16 h-[calc(100vh-2.5rem)] w-full rounded-lg bg-background p-6 shadow-lg">
<div className="mb-4 flex items-center justify-between">
<div className="flex items-center">
<FdBadge
className={`rounded-sm border p-1 text-xs font-light transition-colors duration-200 ${
allSelected
? "border border-transparent bg-fd-badge text-white hover:border-fd-badge-border-hover hover:bg-fd-badge-hover"
: "border-fd-badge-inactive-border bg-[var(--foreground)] text-[var(--muted-foreground)] hover:border-fd-badge-inactive-border-hover hover:bg-fd-badge-inactive hover:text-fd-badge-inactive-hover"
}`}
onClick={toggleAllResources}
>
AII
</FdBadge>
<FdSeparator orientation="vertical" className="mx-2 h-7" />
{Object.values(demoResourceNames).map((resource) => (
<FdBadge
key={resource}
onClick={() => toggleResource(resource)}
className={`ml-1 rounded-sm border p-1 text-xs font-light transition-colors duration-200 ${
selectedResourceNames.length ===
Object.values(demoResourceNames).length
? "border-fd-badge-inactive-border bg-[var(--foreground)] text-[var(--muted-foreground)] hover:border-fd-badge-inactive-border-hover hover:bg-fd-badge-inactive hover:text-fd-badge-inactive-hover"
: selectedResourceNames.includes(resource)
? "border border-transparent bg-fd-badge text-white hover:border-fd-badge-border-hover hover:bg-fd-badge-hover"
: "border-fd-badge-inactive-border bg-[var(--foreground)] text-[var(--muted-foreground)] hover:border-fd-badge-inactive-border-hover hover:bg-fd-badge-inactive hover:text-fd-badge-inactive-hover"
}`}
>
{resource}
</FdBadge>
))}
</div>
<div
className="text-[var(--subtle-700)] hover:text-[var(--subtle-500)] focus:outline-none"
onClick={() => {
setIsOpenSearchWindow(false);
setSearchTextState("");
}}
>
<FdIcon name="cross2" size="medium" className="size-6 cursor-pointer" />
</div>
</div>
<hr className="mb-5" />
<div className="mt-4 h-[calc(100vh-12.5rem)] overflow-y-auto">
{Object.keys(searchResults).length === 0 ||
Object.values(searchResults).every((res) => res.length === 0) ? (
<div className="flex h-full flex-col items-center justify-center text-center">
<Image
src={notFoundImg}
alt="No Results"
width={300}
height={200}
className="mb-4 object-cover"
priority
/>
<h5>No results found.</h5>
</div>
) : (
Object.entries(searchResults).map(([resourceName, resources]) => {
if (resources.length === 0) {
return null;
}
return (
<div className="mb-5" key={resourceName}>
<FdSearchResults
resourceName={resourceName}
items={resources}
displayMode={tableDisplayModes.minimal}
/>
</div>
);
})
)}
</div>
</div>
</div>
)}
</>
);
}
Editor is loading...
Leave a Comment