Better request delete and formatting

This commit is contained in:
Gregory Schier
2023-03-15 09:41:38 -07:00
parent 97db32fdf2
commit 3bbe9d9201
7 changed files with 76 additions and 41 deletions

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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',
)}
/>

View File

@@ -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