Untitled
unknown
plain_text
5 months ago
9.5 kB
4
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