mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-24 16:45:02 +01:00
Codemirror initial value support
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/tauri';
|
||||
import Editor from './components/Editor/Editor';
|
||||
import { HStack, VStack } from './components/Stacks';
|
||||
@@ -8,6 +8,7 @@ import { IconButton } from './components/IconButton';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
import { UrlBar } from './components/UrlBar';
|
||||
import { Grid } from './components/Grid';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface Response {
|
||||
url: string;
|
||||
@@ -20,16 +21,18 @@ interface Response {
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [response, setResponse] = useState<Response | null>(null);
|
||||
const [url, setUrl] = useState<string>('https://go-server.schier.dev/debug');
|
||||
const [body, setBody] = useState<string>('');
|
||||
const [body, setBody] = useState<string>(`{\n "foo": "bar"\n}`);
|
||||
const [method, setMethod] = useState<{ label: string; value: string }>({
|
||||
label: 'GET',
|
||||
value: 'GET',
|
||||
});
|
||||
|
||||
async function sendRequest() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
@@ -44,11 +47,23 @@ function App() {
|
||||
setResponse(resp);
|
||||
} catch (err) {
|
||||
setError(`${err}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const contentType = response?.headers['content-type']?.split(';')[0] ?? 'text/plain';
|
||||
|
||||
useEffect(() => {
|
||||
const listener = async (e: KeyboardEvent) => {
|
||||
if (e.metaKey && (e.key === 'Enter' || e.key === 'r')) {
|
||||
await sendRequest();
|
||||
}
|
||||
};
|
||||
document.documentElement.addEventListener('keypress', listener);
|
||||
return () => document.documentElement.removeEventListener('keypress', listener);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-[auto_1fr] h-full">
|
||||
@@ -60,15 +75,12 @@ function App() {
|
||||
<UrlBar
|
||||
method={method}
|
||||
url={url}
|
||||
loading={loading}
|
||||
onMethodChange={setMethod}
|
||||
onUrlChange={setUrl}
|
||||
sendRequest={sendRequest}
|
||||
/>
|
||||
<Editor
|
||||
value={`{\n "foo": "bar"\n}`}
|
||||
contentType="application/json"
|
||||
onChange={setBody}
|
||||
/>
|
||||
<Editor initialValue={body} contentType="application/json" onChange={setBody} />
|
||||
</VStack>
|
||||
</VStack>
|
||||
<VStack className="w-full">
|
||||
@@ -88,32 +100,40 @@ function App() {
|
||||
<IconButton icon="gear" className="ml-auto" size="sm" />
|
||||
</Dropdown>
|
||||
</HStack>
|
||||
<VStack className="pr-3 pl-1.5 py-3" space={3}>
|
||||
{error && <div className="text-white bg-red-500 px-3 py-1 rounded">{error}</div>}
|
||||
{response !== null && (
|
||||
<>
|
||||
<HStack
|
||||
items="center"
|
||||
className="italic text-gray-500 text-sm w-full pointer-events-none h-10 mb-3 flex-shrink-0"
|
||||
>
|
||||
{response.status}
|
||||
•
|
||||
{response.elapsed}ms •
|
||||
{response.elapsed2}ms
|
||||
</HStack>
|
||||
{contentType.includes('html') ? (
|
||||
<iframe
|
||||
title="Response preview"
|
||||
srcDoc={response.body}
|
||||
sandbox="allow-scripts allow-same-origin"
|
||||
className="h-full w-full rounded-lg"
|
||||
/>
|
||||
) : response?.body ? (
|
||||
<Editor value={response?.body} contentType={contentType} />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</VStack>
|
||||
{(response || error) && (
|
||||
<motion.div
|
||||
animate={{ opacity: 1 }}
|
||||
initial={{ opacity: 0 }}
|
||||
className="w-full h-full"
|
||||
>
|
||||
<VStack className="pr-3 pl-1.5 py-3" space={3}>
|
||||
{error && <div className="text-white bg-red-500 px-3 py-1 rounded">{error}</div>}
|
||||
{response && (
|
||||
<>
|
||||
<HStack
|
||||
items="center"
|
||||
className="italic text-gray-500 text-sm w-full pointer-events-none h-10 mb-3 flex-shrink-0"
|
||||
>
|
||||
{response.status}
|
||||
•
|
||||
{response.elapsed}ms •
|
||||
{response.elapsed2}ms
|
||||
</HStack>
|
||||
{contentType.includes('html') ? (
|
||||
<iframe
|
||||
title="Response preview"
|
||||
srcDoc={response.body}
|
||||
sandbox="allow-scripts allow-same-origin"
|
||||
className="h-full w-full rounded-lg"
|
||||
/>
|
||||
) : response?.body ? (
|
||||
<Editor value={response?.body} contentType={contentType} />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</VStack>
|
||||
</motion.div>
|
||||
)}
|
||||
</VStack>
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
@@ -3,15 +3,12 @@ import './Editor.css';
|
||||
|
||||
interface Props {
|
||||
contentType: string;
|
||||
value: string;
|
||||
initialValue?: string;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
export default function Editor(props: Props) {
|
||||
const { ref } = useCodeMirror({
|
||||
value: props.value,
|
||||
contentType: props.contentType,
|
||||
onChange: props.onChange,
|
||||
});
|
||||
const { ref } = useCodeMirror(props);
|
||||
return <div ref={ref} className="cm-wrapper" />;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
import { HStack } from './Stacks';
|
||||
import { DropdownMenuRadio } from './Dropdown';
|
||||
import { Button } from './Button';
|
||||
import { Input } from './Input';
|
||||
import { FormEvent, useState } from 'react';
|
||||
import { FormEvent } from 'react';
|
||||
import { IconButton } from './IconButton';
|
||||
|
||||
interface Props {
|
||||
sendRequest: () => Promise<void>;
|
||||
sendRequest: () => void;
|
||||
loading: boolean;
|
||||
method: { label: string; value: string };
|
||||
url: string;
|
||||
onMethodChange: (method: { label: string; value: string }) => void;
|
||||
onUrlChange: (url: string) => void;
|
||||
}
|
||||
|
||||
export function UrlBar({ sendRequest, onMethodChange, method, onUrlChange, url }: Props) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
export function UrlBar({ sendRequest, loading, onMethodChange, method, onUrlChange, url }: Props) {
|
||||
const handleSendRequest = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (loading) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
await sendRequest();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
sendRequest();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -96,11 +96,13 @@ const extensions = [
|
||||
];
|
||||
|
||||
export default function useCodeMirror({
|
||||
initialValue,
|
||||
value,
|
||||
contentType,
|
||||
onChange,
|
||||
}: {
|
||||
value: string;
|
||||
initialValue?: string;
|
||||
value?: string;
|
||||
contentType: string;
|
||||
onChange?: (value: string) => void;
|
||||
}) {
|
||||
@@ -108,9 +110,12 @@ export default function useCodeMirror({
|
||||
const ref = useRef(null);
|
||||
useEffect(() => {
|
||||
if (ref.current === null) return;
|
||||
|
||||
const view = new EditorView({
|
||||
const state = EditorState.create({
|
||||
doc: initialValue,
|
||||
extensions: getExtensions({ contentType, onChange }),
|
||||
});
|
||||
const view = new EditorView({
|
||||
state,
|
||||
parent: ref.current,
|
||||
});
|
||||
|
||||
@@ -123,11 +128,11 @@ export default function useCodeMirror({
|
||||
if (cm === null) return;
|
||||
|
||||
const newState = EditorState.create({
|
||||
doc: value,
|
||||
doc: value ?? cm.state.doc,
|
||||
extensions: getExtensions({ contentType, onChange }),
|
||||
});
|
||||
cm.setState(newState);
|
||||
}, [cm, value]);
|
||||
}, [cm, contentType, value, onChange]);
|
||||
|
||||
return { ref, cm };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user