import classNames from 'classnames'; import type { ReactNode } from 'react'; import { useCallback, useMemo } from 'react'; import { useDebouncedState } from '../../hooks/useDebouncedState'; import { useFilterResponse } from '../../hooks/useFilterResponse'; import { useResponseBodyText } from '../../hooks/useResponseBodyText'; import { useResponseContentType } from '../../hooks/useResponseContentType'; import { useToggle } from '../../hooks/useToggle'; import { tryFormatJson } from '../../lib/formatters'; import type { HttpResponse } from '../../lib/models'; import { Editor } from '../core/Editor'; import { IconButton } from '../core/IconButton'; import { Input } from '../core/Input'; interface Props { response: HttpResponse; pretty: boolean; } export function TextViewer({ response, pretty }: Props) { const [isSearching, toggleIsSearching] = useToggle(); const [filterText, setDebouncedFilterText, setFilterText] = useDebouncedState('', 400); const contentType = useResponseContentType(response); const rawBody = useResponseBodyText(response) ?? ''; const formattedBody = pretty && contentType?.includes('json') ? tryFormatJson(rawBody) : rawBody; const filteredResponse = useFilterResponse({ filter: filterText, responseId: response.id }); const body = filteredResponse ?? formattedBody; const clearSearch = useCallback(() => { toggleIsSearching(); setFilterText(''); }, [setFilterText, toggleIsSearching]); const isJson = contentType?.includes('json'); const isXml = contentType?.includes('xml') || contentType?.includes('html'); const canFilter = isJson || isXml; const actions = useMemo(() => { const result: ReactNode[] = []; if (!canFilter) return result; if (isSearching) { result.push(
e.key === 'Escape' && clearSearch()} onChange={setDebouncedFilterText} />
, ); } result.push( , ); return result; }, [canFilter, clearSearch, filterText, isJson, isSearching, setDebouncedFilterText]); return ( ); }