mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-18 15:09:45 +02:00
Custom HTTP method names
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
import { memo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
|
import { usePrompt } from '../hooks/usePrompt';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
|
import type { DropdownItem } from './core/Dropdown';
|
||||||
|
import { Icon } from './core/Icon';
|
||||||
|
import type { RadioDropdownItem } from './core/RadioDropdown';
|
||||||
import { RadioDropdown } from './core/RadioDropdown';
|
import { RadioDropdown } from './core/RadioDropdown';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -8,7 +12,15 @@ type Props = {
|
|||||||
onChange: (method: string) => void;
|
onChange: (method: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const methodItems = ['GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'].map((m) => ({
|
const radioItems: RadioDropdownItem<string>[] = [
|
||||||
|
'GET',
|
||||||
|
'PUT',
|
||||||
|
'POST',
|
||||||
|
'PATCH',
|
||||||
|
'DELETE',
|
||||||
|
'OPTIONS',
|
||||||
|
'HEAD',
|
||||||
|
].map((m) => ({
|
||||||
value: m,
|
value: m,
|
||||||
label: m,
|
label: m,
|
||||||
}));
|
}));
|
||||||
@@ -18,8 +30,32 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
|||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const prompt = usePrompt();
|
||||||
|
const extraItems = useMemo<DropdownItem[]>(
|
||||||
|
() => [
|
||||||
|
{ type: 'separator' },
|
||||||
|
{
|
||||||
|
key: 'custom',
|
||||||
|
label: 'CUSTOM',
|
||||||
|
leftSlot: <Icon icon="sparkles" />,
|
||||||
|
onSelect: async () => {
|
||||||
|
const newMethod = await prompt({
|
||||||
|
label: 'Http Method',
|
||||||
|
name: 'httpMethod',
|
||||||
|
defaultValue: '',
|
||||||
|
title: 'Custom Method',
|
||||||
|
description: 'Enter a custom method name',
|
||||||
|
placeholder: 'CUSTOM',
|
||||||
|
});
|
||||||
|
onChange(newMethod);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[onChange, prompt],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RadioDropdown value={method} items={methodItems} onChange={onChange}>
|
<RadioDropdown value={method} items={radioItems} extraItems={extraItems} onChange={onChange}>
|
||||||
<Button size="xs" className={className}>
|
<Button size="xs" className={className}>
|
||||||
{method.toUpperCase()}
|
{method.toUpperCase()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const SettingsDialog = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack space={2}>
|
<VStack space={2}>
|
||||||
<HStack alignItems="center" space={2}>
|
<HStack className="mt-1" alignItems="center" space={2}>
|
||||||
<div className="w-1/3">Appearance</div>
|
<div className="w-1/3">Appearance</div>
|
||||||
<select
|
<select
|
||||||
value={settings.appearance}
|
value={settings.appearance}
|
||||||
@@ -42,11 +42,11 @@ export const SettingsDialog = () => {
|
|||||||
|
|
||||||
<Heading size={2}>
|
<Heading size={2}>
|
||||||
Workspace{' '}
|
Workspace{' '}
|
||||||
<div className="inline-block ml-1 bg-gray-500 dark:bg-gray-300 px-2 py-0.5 !text-md rounded text-base text-white dark:text-gray-900">
|
<div className="inline-block ml-1 bg-gray-500 dark:bg-gray-300 px-2 py-0.5 text-sm rounded text-white dark:text-gray-900">
|
||||||
{workspace.name}
|
{workspace.name}
|
||||||
</div>
|
</div>
|
||||||
</Heading>
|
</Heading>
|
||||||
<VStack className="w-full" space={3}>
|
<VStack className="mt-1 w-full" space={3}>
|
||||||
<Input
|
<Input
|
||||||
size="xs"
|
size="xs"
|
||||||
name="requestTimeout"
|
name="requestTimeout"
|
||||||
|
|||||||
@@ -656,12 +656,6 @@ const SidebarItem = forwardRef(function SidebarItem(
|
|||||||
leftSlot: <Icon icon="sendHorizontal" />,
|
leftSlot: <Icon icon="sendHorizontal" />,
|
||||||
onSelect: () => sendRequest.mutate(),
|
onSelect: () => sendRequest.mutate(),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: 'sendAndDownloadRequest',
|
|
||||||
label: 'Send and Download',
|
|
||||||
leftSlot: <Icon icon="download" />,
|
|
||||||
onSelect: () => sendAndDownloadRequest.mutate(),
|
|
||||||
},
|
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
key: 'duplicateRequest',
|
key: 'duplicateRequest',
|
||||||
|
|||||||
@@ -1,63 +1,64 @@
|
|||||||
import * as lucide from 'lucide-react';
|
import * as lucide from 'lucide-react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type {HTMLAttributes} from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
import {memo} from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
archive: lucide.ArchiveIcon,
|
archive: lucide.ArchiveIcon,
|
||||||
box: lucide.BoxIcon,
|
box: lucide.BoxIcon,
|
||||||
chat: lucide.MessageSquare,
|
chat: lucide.MessageSquare,
|
||||||
check: lucide.CheckIcon,
|
check: lucide.CheckIcon,
|
||||||
chevronDown: lucide.ChevronDownIcon,
|
chevronDown: lucide.ChevronDownIcon,
|
||||||
chevronRight: lucide.ChevronRightIcon,
|
chevronRight: lucide.ChevronRightIcon,
|
||||||
code: lucide.CodeIcon,
|
code: lucide.CodeIcon,
|
||||||
copy: lucide.CopyIcon,
|
copy: lucide.CopyIcon,
|
||||||
download: lucide.DownloadIcon,
|
download: lucide.DownloadIcon,
|
||||||
externalLink: lucide.ExternalLinkIcon,
|
externalLink: lucide.ExternalLinkIcon,
|
||||||
eye: lucide.EyeIcon,
|
eye: lucide.EyeIcon,
|
||||||
eyeClosed: lucide.EyeOffIcon,
|
eyeClosed: lucide.EyeOffIcon,
|
||||||
filter: lucide.FilterIcon,
|
filter: lucide.FilterIcon,
|
||||||
flask: lucide.FlaskConicalIcon,
|
flask: lucide.FlaskConicalIcon,
|
||||||
gripVertical: lucide.GripVerticalIcon,
|
gripVertical: lucide.GripVerticalIcon,
|
||||||
keyboard: lucide.KeyboardIcon,
|
keyboard: lucide.KeyboardIcon,
|
||||||
leftPanelHidden: lucide.PanelLeftOpenIcon,
|
leftPanelHidden: lucide.PanelLeftOpenIcon,
|
||||||
leftPanelVisible: lucide.PanelLeftCloseIcon,
|
leftPanelVisible: lucide.PanelLeftCloseIcon,
|
||||||
magicWand: lucide.Wand2Icon,
|
magicWand: lucide.Wand2Icon,
|
||||||
moreVertical: lucide.MoreVerticalIcon,
|
moreVertical: lucide.MoreVerticalIcon,
|
||||||
pencil: lucide.PencilIcon,
|
pencil: lucide.PencilIcon,
|
||||||
plus: lucide.PlusIcon,
|
plus: lucide.PlusIcon,
|
||||||
plusCircle: lucide.PlusCircleIcon,
|
plusCircle: lucide.PlusCircleIcon,
|
||||||
question: lucide.ShieldQuestionIcon,
|
question: lucide.ShieldQuestionIcon,
|
||||||
sendHorizontal: lucide.SendHorizonalIcon,
|
sendHorizontal: lucide.SendHorizonalIcon,
|
||||||
settings2: lucide.Settings2Icon,
|
settings2: lucide.Settings2Icon,
|
||||||
settings: lucide.SettingsIcon,
|
settings: lucide.SettingsIcon,
|
||||||
trash: lucide.TrashIcon,
|
sparkles: lucide.SparklesIcon,
|
||||||
update: lucide.RefreshCcwIcon,
|
trash: lucide.TrashIcon,
|
||||||
upload: lucide.UploadIcon,
|
update: lucide.RefreshCcwIcon,
|
||||||
x: lucide.XIcon,
|
upload: lucide.UploadIcon,
|
||||||
|
x: lucide.XIcon,
|
||||||
|
|
||||||
empty: (props: HTMLAttributes<HTMLSpanElement>) => <span {...props} />,
|
empty: (props: HTMLAttributes<HTMLSpanElement>) => <span {...props} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IconProps {
|
export interface IconProps {
|
||||||
icon: keyof typeof icons;
|
icon: keyof typeof icons;
|
||||||
className?: string;
|
className?: string;
|
||||||
size?: 'xs' | 'sm' | 'md';
|
size?: 'xs' | 'sm' | 'md';
|
||||||
spin?: boolean;
|
spin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon = memo(function Icon({icon, spin, size = 'md', className}: IconProps) {
|
export const Icon = memo(function Icon({ icon, spin, size = 'md', className }: IconProps) {
|
||||||
const Component = icons[icon] ?? icons.question;
|
const Component = icons[icon] ?? icons.question;
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
'text-inherit',
|
'text-inherit',
|
||||||
size === 'md' && 'h-4 w-4',
|
size === 'md' && 'h-4 w-4',
|
||||||
size === 'sm' && 'h-3.5 w-3.5',
|
size === 'sm' && 'h-3.5 w-3.5',
|
||||||
size === 'xs' && 'h-3 w-3',
|
size === 'xs' && 'h-3 w-3',
|
||||||
spin && 'animate-spin',
|
spin && 'animate-spin',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,18 +16,20 @@ export interface RadioDropdownProps<T = string | null> {
|
|||||||
value: T;
|
value: T;
|
||||||
onChange: (value: T) => void;
|
onChange: (value: T) => void;
|
||||||
items: RadioDropdownItem<T>[];
|
items: RadioDropdownItem<T>[];
|
||||||
|
extraItems?: DropdownProps['items'];
|
||||||
children: DropdownProps['children'];
|
children: DropdownProps['children'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RadioDropdown<T = string | null>({
|
export function RadioDropdown<T = string | null>({
|
||||||
value,
|
value,
|
||||||
items,
|
items,
|
||||||
|
extraItems,
|
||||||
onChange,
|
onChange,
|
||||||
children,
|
children,
|
||||||
}: RadioDropdownProps<T>) {
|
}: RadioDropdownProps<T>) {
|
||||||
const dropdownItems = useMemo(
|
const dropdownItems = useMemo(
|
||||||
() =>
|
() => [
|
||||||
items.map((item) => {
|
...items.map((item) => {
|
||||||
if (item.type === 'separator') {
|
if (item.type === 'separator') {
|
||||||
return item;
|
return item;
|
||||||
} else {
|
} else {
|
||||||
@@ -40,7 +42,9 @@ export function RadioDropdown<T = string | null>({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[items, value, onChange],
|
...(extraItems ?? []),
|
||||||
|
],
|
||||||
|
[items, extraItems, value, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <Dropdown items={dropdownItems}>{children}</Dropdown>;
|
return <Dropdown items={dropdownItems}>{children}</Dropdown>;
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ export interface PromptProps {
|
|||||||
onResult: (value: string) => void;
|
onResult: (value: string) => void;
|
||||||
label: InputProps['label'];
|
label: InputProps['label'];
|
||||||
name: InputProps['name'];
|
name: InputProps['name'];
|
||||||
defaultValue: InputProps['defaultValue'];
|
defaultValue?: InputProps['defaultValue'];
|
||||||
|
placeholder?: InputProps['placeholder'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Prompt({ onHide, label, name, defaultValue, onResult }: PromptProps) {
|
export function Prompt({ onHide, label, name, defaultValue, placeholder, onResult }: PromptProps) {
|
||||||
const [value, setValue] = useState<string>(defaultValue ?? '');
|
const [value, setValue] = useState<string>(defaultValue ?? '');
|
||||||
const handleSubmit = useCallback(
|
const handleSubmit = useCallback(
|
||||||
(e: FormEvent<HTMLFormElement>) => {
|
(e: FormEvent<HTMLFormElement>) => {
|
||||||
@@ -33,6 +34,7 @@ export function Prompt({ onHide, label, name, defaultValue, onResult }: PromptPr
|
|||||||
hideLabel
|
hideLabel
|
||||||
require
|
require
|
||||||
autoSelect
|
autoSelect
|
||||||
|
placeholder={placeholder}
|
||||||
label={label}
|
label={label}
|
||||||
name={name}
|
name={name}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { dialog } from '@tauri-apps/api';
|
||||||
import type { DialogProps } from '../components/core/Dialog';
|
import type { DialogProps } from '../components/core/Dialog';
|
||||||
import { useDialog } from '../components/DialogContext';
|
import { useDialog } from '../components/DialogContext';
|
||||||
import type { PromptProps } from './Prompt';
|
import type { PromptProps } from './Prompt';
|
||||||
@@ -11,20 +12,17 @@ export function usePrompt() {
|
|||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
}: {
|
placeholder,
|
||||||
title: DialogProps['title'];
|
}: Pick<DialogProps, 'title' | 'description'> &
|
||||||
description?: DialogProps['description'];
|
Pick<PromptProps, 'name' | 'label' | 'defaultValue' | 'placeholder'>) =>
|
||||||
name: PromptProps['name'];
|
|
||||||
label: PromptProps['label'];
|
|
||||||
defaultValue: PromptProps['defaultValue'];
|
|
||||||
}) =>
|
|
||||||
new Promise((onResult: PromptProps['onResult']) => {
|
new Promise((onResult: PromptProps['onResult']) => {
|
||||||
dialog.show({
|
dialog.show({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
hideX: true,
|
hideX: true,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
render: ({ hide }) => Prompt({ onHide: hide, onResult, name, label, defaultValue }),
|
render: ({ hide }) =>
|
||||||
|
Prompt({ onHide: hide, onResult, name, label, defaultValue, placeholder }),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,22 @@ export function generateColors(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lightnessMap: Record<Appearance, Record<AppThemeColorVariant, number>> = {
|
const lightnessMap: Record<Appearance, Record<AppThemeColorVariant, number>> = {
|
||||||
|
system: {
|
||||||
|
// Not actually used
|
||||||
|
0: 1,
|
||||||
|
50: 1,
|
||||||
|
100: 0.9,
|
||||||
|
200: 0.7,
|
||||||
|
300: 0.4,
|
||||||
|
400: 0.2,
|
||||||
|
500: 0,
|
||||||
|
600: -0.2,
|
||||||
|
700: -0.4,
|
||||||
|
800: -0.6,
|
||||||
|
900: -0.8,
|
||||||
|
950: -0.9,
|
||||||
|
1000: -1,
|
||||||
|
},
|
||||||
light: {
|
light: {
|
||||||
0: 1,
|
0: 1,
|
||||||
50: 1,
|
50: 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user