export function SearchWindow()

in frontend/apps/quantgrid/src/app/components/SearchWindow/SearchWindow.tsx [32:307]


export function SearchWindow() {
  const {
    isOpen,
    closeSearchWindow,
    filter,
    setFilter,
    searchQuery,
    setSearchQuery,
  } = useContext(SearchWindowContext);
  const {
    projectName,
    projectBucket,
    projectPath,
    openSheet,
    getProjects,
    projects,
    projectSheets,
    parsedSheets,
  } = useContext(ProjectContext);
  const navigate = useNavigate();
  const { openField, openTable } = useContext(AppSpreadsheetInteractionContext);

  const inputRef = useRef<InputRef>(null);

  const [currentChosenIndex, setCurrentChosenIndex] = useState<number>(0);
  const resultCountRef = useRef(0);

  useEffect(() => {
    getProjects();
    if (!inputRef.current?.input) return;

    setTimeout(() => inputRef.current?.input?.focus());
  }, [getProjects, isOpen]);

  const results: Fuse.FuseResult<ISearchResult>[] | null = useMemo(
    () =>
      isOpen && projectBucket
        ? search(
            projects,
            projectSheets,
            parsedSheets,
            searchQuery,
            filter,
            projectBucket,
            projectPath
          )
        : null,
    [
      filter,
      isOpen,
      parsedSheets,
      projectBucket,
      projectPath,
      projectSheets,
      projects,
      searchQuery,
    ]
  );

  const onSubmit = useCallback(
    (result: ISearchResult) => {
      closeSearchWindow();
      switch (result.type) {
        case 'project': {
          navigate(
            getProjectNavigateUrl({
              projectName: result.path.projectName,
              projectBucket: result.path.projectBucket,
              projectPath: result.path.projectPath,
            })
          );

          break;
        }
        case 'sheet': {
          if (!projectName) return;

          openSheet({ sheetName: result.name });

          break;
        }
        case 'table': {
          if (!result.path.sheetName || !result.path.tableName) return;

          openTable(result.path.sheetName, result.path.tableName);

          break;
        }
        case 'field': {
          if (
            !result.path.sheetName ||
            !result.path.tableName ||
            !result.path.fieldName
          ) {
            return;
          }
          openField(
            result.path.sheetName,
            result.path.tableName,
            result.path.fieldName
          );

          break;
        }
      }
    },
    [closeSearchWindow, navigate, openField, openSheet, openTable, projectName]
  );

  const onKeydown = useCallback((event: React.KeyboardEvent) => {
    if (
      event.key === KeyboardCode.Delete ||
      event.key === KeyboardCode.Backspace
    ) {
      event.stopPropagation();
    }
  }, []);

  useEffect(() => {
    const handleArrows = (event: KeyboardEvent) => {
      if (event.key === KeyboardCode.ArrowUp) {
        setCurrentChosenIndex((value) => {
          if (value === 0) return 0;

          return value - 1;
        });
        event.preventDefault();
      }
      if (event.key === KeyboardCode.ArrowDown) {
        setCurrentChosenIndex((value) => {
          return Math.min(value + 1, resultCountRef.current);
        });
        event.preventDefault();
      }
    };

    window.addEventListener('keydown', handleArrows);

    return () => {
      window.removeEventListener('keydown', handleArrows);
    };
  }, []);

  useEffect(() => {
    if (!results || !results.length) {
      resultCountRef.current = 0;

      return;
    }

    resultCountRef.current = results.length - 1;
    setCurrentChosenIndex(0);
  }, [results]);

  useEffect(() => {
    const handleSubmit = (event: KeyboardEvent) => {
      if (
        event.key === KeyboardCode.Enter &&
        currentChosenIndex !== null &&
        results
      ) {
        const currentItem = results[currentChosenIndex].item;
        onSubmit(currentItem);
      }
    };

    window.addEventListener('keydown', handleSubmit);

    return () => {
      window.removeEventListener('keydown', handleSubmit);
    };
  }, [currentChosenIndex, onSubmit, results]);

  useEffect(() => {
    const handleSwitchFilter = (event: KeyboardEvent) => {
      if (!isOpen) return;

      const isTab = event.code === KeyboardCode.Tab;
      const isShiftTab = shortcutApi.is(Shortcut.MoveTabBackward, event);

      if (!isTab && !isShiftTab) return;

      event.preventDefault();

      const currentTab = searchFilterTabs.findIndex((f) => f === filter);

      if (isShiftTab) {
        const nextTab =
          currentTab - 1 < 0
            ? searchFilterTabs[searchFilterTabs.length - 1]
            : searchFilterTabs[currentTab - 1];

        setFilter(nextTab);
      } else if (isTab) {
        const nextTab =
          currentTab + 1 >= searchFilterTabs.length
            ? searchFilterTabs[0]
            : searchFilterTabs[currentTab + 1];

        setFilter(nextTab);
      }
    };

    window.addEventListener('keydown', handleSwitchFilter);

    return () => {
      window.removeEventListener('keydown', handleSwitchFilter);
    };
  }, [currentChosenIndex, filter, isOpen, onSubmit, results, setFilter]);

  return (
    <div className="w-full h-full pb-5">
      <Input
        className={cx('ant-input-sm h-[38px] text-[13px]', inputClasses)}
        placeholder="Search..."
        prefix={
          <div className="pr-2 stroke-textSecondary">
            <SearchIcon />
          </div>
        }
        ref={inputRef}
        value={searchQuery}
        onChange={(e) => setSearchQuery(e.target.value)}
        onKeyDown={onKeydown}
      />
      <div className="flex items-center mt-5 mb-4">
        <SearchFilter
          filterName="All"
          selected={filter === null}
          onClick={() => setFilter(null)}
        />
        <SearchFilter
          filterName="Projects"
          selected={filter === 'projects'}
          onClick={() => setFilter('projects')}
        />
        <SearchFilter
          filterName="Sheets"
          selected={filter === 'sheets'}
          onClick={() => setFilter('sheets')}
        />
        <SearchFilter
          filterName="Tables"
          selected={filter === 'tables'}
          onClick={() => setFilter('tables')}
        />
        <SearchFilter
          filterName="Fields"
          selected={filter === 'fields'}
          onClick={() => setFilter('fields')}
        />
        <span className="text-[10px] text-textSecondary">
          Tab or Shift+Tab to switch
        </span>
      </div>
      <div className="py-2 pr-2 overflow-auto h-max max-h-96 bg-bgLayer3">
        {results && results.length === 0 && (
          <div className="text-textPrimary pl-3">No results.</div>
        )}
        {results?.map((result, index) => (
          <SearchResult
            className={cx(
              'p-2 mb-1 cursor-pointer rounded-[3px] border-b-strokeTertiary select-none hover:bg-bgAccentPrimaryAlpha',
              index === currentChosenIndex
                ? 'border-l-2 border-l-strokeAccentPrimary bg-bgAccentPrimaryAlpha stroke-textPrimary'
                : 'stroke-textSecondary'
            )}
            key={result.item.name + result.item.type + index}
            result={result}
            onClick={() => onSubmit(result.item)}
          />
        ))}
      </div>
    </div>
  );
}