Untitled

 avatar
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