mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-20 15:51:23 +02:00
Move responses dropdown to separate component
This commit is contained in:
64
src-web/components/RecentResponsesDropdown.tsx
Normal file
64
src-web/components/RecentResponsesDropdown.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { useDeleteResponse } from '../hooks/useDeleteResponse';
|
||||||
|
import { useDeleteResponses } from '../hooks/useDeleteResponses';
|
||||||
|
import type { HttpResponse } from '../lib/models';
|
||||||
|
import { Dropdown } from './core/Dropdown';
|
||||||
|
import { pluralize } from '../lib/pluralize';
|
||||||
|
import { HStack } from './core/Stacks';
|
||||||
|
import { StatusTag } from './core/StatusTag';
|
||||||
|
import { Icon } from './core/Icon';
|
||||||
|
import { IconButton } from './core/IconButton';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
responses: HttpResponse[];
|
||||||
|
activeResponse: HttpResponse;
|
||||||
|
onPinnedResponse: (r: HttpResponse) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RecentResponsesDropdown = function ResponsePane({
|
||||||
|
activeResponse,
|
||||||
|
responses,
|
||||||
|
onPinnedResponse,
|
||||||
|
}: Props) {
|
||||||
|
const deleteResponse = useDeleteResponse(activeResponse?.id ?? null);
|
||||||
|
const deleteAllResponses = useDeleteResponses(activeResponse?.requestId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'clear-single',
|
||||||
|
label: 'Clear Response',
|
||||||
|
onSelect: deleteResponse.mutate,
|
||||||
|
disabled: responses.length === 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clear-all',
|
||||||
|
label: `Clear ${responses.length} ${pluralize('Response', responses.length)}`,
|
||||||
|
onSelect: deleteAllResponses.mutate,
|
||||||
|
hidden: responses.length <= 1,
|
||||||
|
disabled: responses.length === 0,
|
||||||
|
},
|
||||||
|
{ type: 'separator', label: 'History' },
|
||||||
|
...responses.slice(0, 20).map((r) => ({
|
||||||
|
key: r.id,
|
||||||
|
label: (
|
||||||
|
<HStack space={2}>
|
||||||
|
<StatusTag className="text-xs" response={r} />
|
||||||
|
<span>•</span> <span>{r.elapsed}ms</span>
|
||||||
|
</HStack>
|
||||||
|
),
|
||||||
|
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <Icon icon="empty" />,
|
||||||
|
onSelect: () => onPinnedResponse(r),
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
title="Show response history"
|
||||||
|
icon="triangleDown"
|
||||||
|
className="ml-auto"
|
||||||
|
size="sm"
|
||||||
|
iconSize="md"
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,23 +1,17 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { memo, useEffect, useMemo, useState } from 'react';
|
import { useCallback, memo, useEffect, useMemo, useState } from 'react';
|
||||||
import { createGlobalState } from 'react-use';
|
import { createGlobalState } from 'react-use';
|
||||||
import { useActiveRequestId } from '../hooks/useActiveRequestId';
|
import { useActiveRequestId } from '../hooks/useActiveRequestId';
|
||||||
import { useDeleteResponse } from '../hooks/useDeleteResponse';
|
|
||||||
import { useDeleteResponses } from '../hooks/useDeleteResponses';
|
|
||||||
import { useLatestResponse } from '../hooks/useLatestResponse';
|
import { useLatestResponse } from '../hooks/useLatestResponse';
|
||||||
import { useResponseContentType } from '../hooks/useResponseContentType';
|
import { useResponseContentType } from '../hooks/useResponseContentType';
|
||||||
import { useResponses } from '../hooks/useResponses';
|
import { useResponses } from '../hooks/useResponses';
|
||||||
import { useResponseViewMode } from '../hooks/useResponseViewMode';
|
import { useResponseViewMode } from '../hooks/useResponseViewMode';
|
||||||
import type { HttpResponse } from '../lib/models';
|
import type { HttpResponse } from '../lib/models';
|
||||||
import { isResponseLoading } from '../lib/models';
|
import { isResponseLoading } from '../lib/models';
|
||||||
import { pluralize } from '../lib/pluralize';
|
|
||||||
import { Banner } from './core/Banner';
|
import { Banner } from './core/Banner';
|
||||||
import { CountBadge } from './core/CountBadge';
|
import { CountBadge } from './core/CountBadge';
|
||||||
import { Dropdown } from './core/Dropdown';
|
|
||||||
import { DurationTag } from './core/DurationTag';
|
import { DurationTag } from './core/DurationTag';
|
||||||
import { Icon } from './core/Icon';
|
|
||||||
import { IconButton } from './core/IconButton';
|
|
||||||
import { SizeTag } from './core/SizeTag';
|
import { SizeTag } from './core/SizeTag';
|
||||||
import { HStack } from './core/Stacks';
|
import { HStack } from './core/Stacks';
|
||||||
import { StatusTag } from './core/StatusTag';
|
import { StatusTag } from './core/StatusTag';
|
||||||
@@ -29,6 +23,7 @@ import { CsvViewer } from './responseViewers/CsvViewer';
|
|||||||
import { ImageViewer } from './responseViewers/ImageViewer';
|
import { ImageViewer } from './responseViewers/ImageViewer';
|
||||||
import { TextViewer } from './responseViewers/TextViewer';
|
import { TextViewer } from './responseViewers/TextViewer';
|
||||||
import { WebPageViewer } from './responseViewers/WebPageViewer';
|
import { WebPageViewer } from './responseViewers/WebPageViewer';
|
||||||
|
import { RecentResponsesDropdown } from './RecentResponsesDropdown';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
@@ -46,8 +41,6 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
|
|||||||
? responses.find((r) => r.id === pinnedResponseId) ?? null
|
? responses.find((r) => r.id === pinnedResponseId) ?? null
|
||||||
: latestResponse ?? null;
|
: latestResponse ?? null;
|
||||||
const [viewMode, setViewMode] = useResponseViewMode(activeResponse?.requestId);
|
const [viewMode, setViewMode] = useResponseViewMode(activeResponse?.requestId);
|
||||||
const deleteResponse = useDeleteResponse(activeResponse?.id ?? null);
|
|
||||||
const deleteAllResponses = useDeleteResponses(activeResponse?.requestId);
|
|
||||||
const [activeTab, setActiveTab] = useActiveTab();
|
const [activeTab, setActiveTab] = useActiveTab();
|
||||||
|
|
||||||
// Unset pinned response when a new one comes in
|
// Unset pinned response when a new one comes in
|
||||||
@@ -55,6 +48,10 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
|
|||||||
|
|
||||||
const contentType = useResponseContentType(activeResponse);
|
const contentType = useResponseContentType(activeResponse);
|
||||||
|
|
||||||
|
const handlePinnedResponse = useCallback((r: HttpResponse) => {
|
||||||
|
setPinnedResponseId(r.id);
|
||||||
|
}, [setPinnedResponseId])
|
||||||
|
|
||||||
const tabs: TabItem[] = useMemo(
|
const tabs: TabItem[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -125,44 +122,11 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
|
|||||||
</HStack>
|
</HStack>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dropdown
|
<RecentResponsesDropdown
|
||||||
items={[
|
responses={responses}
|
||||||
{
|
activeResponse={activeResponse}
|
||||||
key: 'clear-single',
|
onPinnedResponse={handlePinnedResponse}
|
||||||
label: 'Clear Response',
|
/>
|
||||||
onSelect: deleteResponse.mutate,
|
|
||||||
disabled: responses.length === 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'clear-all',
|
|
||||||
label: `Clear ${responses.length} ${pluralize('Response', responses.length)}`,
|
|
||||||
onSelect: deleteAllResponses.mutate,
|
|
||||||
hidden: responses.length <= 1,
|
|
||||||
disabled: responses.length === 0,
|
|
||||||
},
|
|
||||||
{ type: 'separator', label: 'History' },
|
|
||||||
...responses.slice(0, 20).map((r) => ({
|
|
||||||
key: r.id,
|
|
||||||
label: (
|
|
||||||
<HStack space={2}>
|
|
||||||
<StatusTag className="text-xs" response={r} />
|
|
||||||
<span>•</span> <span>{r.elapsed}ms</span>
|
|
||||||
</HStack>
|
|
||||||
),
|
|
||||||
leftSlot:
|
|
||||||
activeResponse?.id === r.id ? <Icon icon="check" /> : <Icon icon="empty" />,
|
|
||||||
onSelect: () => setPinnedResponseId(r.id),
|
|
||||||
})),
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
title="Show response history"
|
|
||||||
icon="triangleDown"
|
|
||||||
className="ml-auto"
|
|
||||||
size="sm"
|
|
||||||
iconSize="md"
|
|
||||||
/>
|
|
||||||
</Dropdown>
|
|
||||||
</HStack>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
Reference in New Issue
Block a user