mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 20:00:29 +01:00
Better request delete and formatting
This commit is contained in:
@@ -441,5 +441,5 @@ fn main() {
|
||||
|
||||
fn is_dev() -> bool {
|
||||
let env = option_env!("YAAK_ENV");
|
||||
env.unwrap_or("production") == "development"
|
||||
env.unwrap_or("production") != "production"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import classnames from 'classnames';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useSendRequest } from '../hooks/useSendRequest';
|
||||
import { useUpdateRequest } from '../hooks/useUpdateRequest';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
import { Editor } from './core/Editor';
|
||||
import { TabContent, Tabs } from './core/Tabs/Tabs';
|
||||
import { GraphQLEditor } from './editors/GraphQLEditor';
|
||||
@@ -71,6 +72,7 @@ export function RequestPane({ fullHeight, className }: Props) {
|
||||
defaultValue={activeRequest.body ?? ''}
|
||||
contentType="application/json"
|
||||
onChange={(body) => updateRequest.mutate({ body })}
|
||||
format={activeRequest.bodyType === 'json' ? (v) => tryFormatJson(v) : undefined}
|
||||
/>
|
||||
) : activeRequest.bodyType === 'graphql' ? (
|
||||
<GraphQLEditor
|
||||
|
||||
@@ -50,7 +50,6 @@ export function Sidebar({ className }: Props) {
|
||||
alignItems="center"
|
||||
justifyContent="end"
|
||||
>
|
||||
<IconButton title="Delete request" icon="trash" onClick={() => deleteRequest.mutate()} />
|
||||
<IconButton
|
||||
title={appearance === 'dark' ? 'Enable light mode' : 'Enable dark mode'}
|
||||
icon={appearance === 'dark' ? 'moon' : 'sun'}
|
||||
@@ -63,6 +62,7 @@ export function Sidebar({ className }: Props) {
|
||||
}
|
||||
|
||||
function SidebarItem({ request, active }: { request: HttpRequest; active: boolean }) {
|
||||
const deleteRequest = useDeleteRequest(request);
|
||||
const updateRequest = useUpdateRequest(request);
|
||||
const [editing, setEditing] = useState<boolean>(false);
|
||||
const handleSubmitNameEdit = async (el: HTMLInputElement) => {
|
||||
@@ -76,7 +76,16 @@ function SidebarItem({ request, active }: { request: HttpRequest; active: boolea
|
||||
};
|
||||
|
||||
return (
|
||||
<li>
|
||||
<li className="group/item relative">
|
||||
<IconButton
|
||||
size="sm"
|
||||
color="custom"
|
||||
iconSize="sm"
|
||||
title="Delete request"
|
||||
icon="trash"
|
||||
className="absolute right-0 transition-opacity opacity-0 group-hover/item:opacity-100"
|
||||
onClick={() => deleteRequest.mutate()}
|
||||
/>
|
||||
<Button
|
||||
color="custom"
|
||||
size="sm"
|
||||
@@ -85,6 +94,9 @@ function SidebarItem({ request, active }: { request: HttpRequest; active: boolea
|
||||
active
|
||||
? 'bg-gray-200/70 text-gray-900'
|
||||
: 'text-gray-600 hover:text-gray-800 active:bg-gray-200/30',
|
||||
|
||||
// Move out of the way when trash is shown
|
||||
'group-hover/item:pr-6',
|
||||
)}
|
||||
onKeyDown={(e) => {
|
||||
// Hitting enter on active request during keyboard nav will start edit
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { Button } from './core/Button';
|
||||
import { Editor } from './core/Editor';
|
||||
import { Heading } from './core/Heading';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
|
||||
export default function Workspaces() {
|
||||
const workspaces = useWorkspaces();
|
||||
@@ -14,7 +13,6 @@ export default function Workspaces() {
|
||||
{w.name}
|
||||
</Button>
|
||||
))}
|
||||
<Editor defaultValue="hello world" />
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { defaultKeymap } from '@codemirror/commands';
|
||||
import { Compartment, EditorState } from '@codemirror/state';
|
||||
import { keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view';
|
||||
import { keymap, placeholder as placeholderExt, tooltips, ViewPlugin } from '@codemirror/view';
|
||||
import classnames from 'classnames';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useUnmount } from 'react-use';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useMount, useUnmount } from 'react-use';
|
||||
import { IconButton } from '../IconButton';
|
||||
import './Editor.css';
|
||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
||||
import { singleLineExt } from './singleLine';
|
||||
@@ -24,6 +25,7 @@ export interface _EditorProps {
|
||||
onChange?: (value: string) => void;
|
||||
onFocus?: () => void;
|
||||
singleLine?: boolean;
|
||||
format?: (v: string) => string;
|
||||
}
|
||||
|
||||
export function _Editor({
|
||||
@@ -38,6 +40,7 @@ export function _Editor({
|
||||
onFocus,
|
||||
className,
|
||||
singleLine,
|
||||
format,
|
||||
}: _EditorProps) {
|
||||
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
|
||||
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -109,18 +112,47 @@ export function _Editor({
|
||||
syncGutterBg({ parent: wrapperRef.current, className });
|
||||
}, [className]);
|
||||
|
||||
const cmContainer = useMemo(
|
||||
() => (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
dangerouslySetInnerHTML={{ __html: '' }}
|
||||
className={classnames(
|
||||
className,
|
||||
'cm-wrapper text-base bg-gray-50',
|
||||
heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height',
|
||||
singleLine ? 'cm-singleline' : 'cm-multiline',
|
||||
readOnly && 'cm-readonly',
|
||||
)}
|
||||
/>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
if (singleLine) {
|
||||
return cmContainer;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
dangerouslySetInnerHTML={{ __html: '' }}
|
||||
className={classnames(
|
||||
className,
|
||||
'cm-wrapper text-base bg-gray-50',
|
||||
heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height',
|
||||
singleLine ? 'cm-singleline' : 'cm-multiline',
|
||||
readOnly && 'cm-readonly',
|
||||
<div className="group relative h-full w-full">
|
||||
{cmContainer}
|
||||
{format && (
|
||||
<IconButton
|
||||
size="sm"
|
||||
title="Re-format"
|
||||
icon="magicWand"
|
||||
className="absolute bottom-2 right-0 transition-opacity opacity-0 group-hover:opacity-70"
|
||||
onClick={() => {
|
||||
if (cm.current === null) return;
|
||||
const { doc } = cm.current.view.state;
|
||||
const insert = format(doc.toString());
|
||||
// Update editor and blur because the cursor will reset anyway
|
||||
cm.current.view.dispatch({ changes: { from: 0, to: doc.length, insert } });
|
||||
cm.current.view.contentDOM.blur();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
TriangleRightIcon,
|
||||
UpdateIcon,
|
||||
RowsIcon,
|
||||
MagicWandIcon,
|
||||
} from '@radix-ui/react-icons';
|
||||
import classnames from 'classnames';
|
||||
|
||||
@@ -48,6 +49,7 @@ const icons = {
|
||||
triangleLeft: TriangleLeftIcon,
|
||||
triangleRight: TriangleRightIcon,
|
||||
update: UpdateIcon,
|
||||
magicWand: MagicWandIcon,
|
||||
x: Cross2Icon,
|
||||
};
|
||||
|
||||
@@ -66,8 +68,8 @@ export function Icon({ icon, spin, size = 'md', className }: IconProps) {
|
||||
className,
|
||||
'text-inherit',
|
||||
size === 'md' && 'h-4 w-4',
|
||||
size === 'sm' && 'h-3 w-3',
|
||||
size === 'xs' && 'h-2 w-2',
|
||||
size === 'sm' && 'h-3.5 w-3.5',
|
||||
size === 'xs' && 'h-3 w-3',
|
||||
spin && 'animate-spin',
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -46,26 +46,15 @@ export function GraphQLEditor({ defaultValue, onChange, ...extraEditorProps }: P
|
||||
|
||||
return (
|
||||
<div className="pb-1 h-full grid grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]">
|
||||
<div className="relative">
|
||||
<Editor
|
||||
key={queryKey.key}
|
||||
heightMode="auto"
|
||||
defaultValue={query ?? ''}
|
||||
onChange={handleChangeQuery}
|
||||
contentType="application/graphql"
|
||||
{...extraEditorProps}
|
||||
/>
|
||||
<IconButton
|
||||
size="sm"
|
||||
title="Re-format GraphQL Query"
|
||||
icon="eye"
|
||||
className="absolute bottom-2 right-0"
|
||||
onClick={() => {
|
||||
handleChangeQuery(formatSdl(query));
|
||||
setTimeout(queryKey.regenerate, 200);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Editor
|
||||
key={queryKey.key}
|
||||
heightMode="auto"
|
||||
defaultValue={query ?? ''}
|
||||
onChange={handleChangeQuery}
|
||||
contentType="application/graphql"
|
||||
format={formatSdl}
|
||||
{...extraEditorProps}
|
||||
/>
|
||||
<Divider />
|
||||
<p className="pt-1 text-gray-500 text-sm">Variables</p>
|
||||
<Editor
|
||||
|
||||
Reference in New Issue
Block a user