More layout fiddling and error page

This commit is contained in:
Gregory Schier
2023-03-04 22:26:00 -08:00
parent 1ecf642181
commit 986cd56662
12 changed files with 96 additions and 40 deletions

View File

@@ -1,7 +1,7 @@
use tauri::{Runtime, Window}; use tauri::{Runtime, Window};
const TRAFFIC_LIGHT_OFFSET_X: f64 = 15.0; const TRAFFIC_LIGHT_OFFSET_X: f64 = 15.0;
const TRAFFIC_LIGHT_OFFSET_Y: f64 = 26.0; const TRAFFIC_LIGHT_OFFSET_Y: f64 = 20.0;
pub trait WindowExt { pub trait WindowExt {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

View File

@@ -1,13 +1,15 @@
import classnames from 'classnames'; import classnames from 'classnames';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { Button } from './components/Button'; import { Button } from './components/Button';
import { Divider } from './components/Divider'; import { Divider } from './components/Divider';
import { Grid } from './components/Grid'; import { Grid } from './components/Grid';
import { IconButton } from './components/IconButton';
import { RequestPane } from './components/RequestPane'; import { RequestPane } from './components/RequestPane';
import { ResponsePane } from './components/ResponsePane'; import { ResponsePane } from './components/ResponsePane';
import { Sidebar } from './components/Sidebar'; import { Sidebar } from './components/Sidebar';
import { HStack } from './components/Stacks'; import { HStack } from './components/Stacks';
import { WindowDragRegion } from './components/WindowDragRegion';
import { import {
useDeleteRequest, useDeleteRequest,
useRequests, useRequests,
@@ -24,6 +26,7 @@ function App() {
const p = useParams<Params>(); const p = useParams<Params>();
const workspaceId = p.workspaceId ?? ''; const workspaceId = p.workspaceId ?? '';
const { data: requests } = useRequests(workspaceId); const { data: requests } = useRequests(workspaceId);
const navigate = useNavigate();
const request = requests?.find((r) => r.id === p.requestId); const request = requests?.find((r) => r.id === p.requestId);
const [screenWidth, setScreenWidth] = useState(window.innerWidth); const [screenWidth, setScreenWidth] = useState(window.innerWidth);
@@ -39,12 +42,20 @@ function App() {
<div className="p-2 h-full"> <div className="p-2 h-full">
<div className="grid grid-rows-[auto_1fr] rounded-md h-full overflow-hidden"> <div className="grid grid-rows-[auto_1fr] rounded-md h-full overflow-hidden">
<HStack <HStack
data-tauri-drag-region as={WindowDragRegion}
className="h-10 px-3 bg-gray-50" className="pl-1 pr-3 bg-gray-50 text-sm"
justify="center" justify="center"
items="center" items="center"
> >
{request.name} <div className="mr-auto">
<IconButton
size="xs"
icon="x"
onClick={() => navigate(`/workspaces/${workspaceId}`)}
/>
</div>
<div>{request.name}</div>
<div className="ml-auto"></div>
</HStack> </HStack>
<div <div
className={classnames( className={classnames(

View File

@@ -10,7 +10,7 @@ import { Icon } from './Icon';
const colorStyles = { const colorStyles = {
default: 'hover:bg-gray-500/10 text-gray-600', default: 'hover:bg-gray-500/10 text-gray-600',
gray: 'bg-gray-50 text-gray-800 hover:bg-gray-500/10', gray: 'text-gray-800 bg-gray-50 hover:bg-gray-500/20',
primary: 'bg-blue-400', primary: 'bg-blue-400',
secondary: 'bg-violet-400', secondary: 'bg-violet-400',
warning: 'bg-orange-400', warning: 'bg-orange-400',

View File

@@ -12,7 +12,7 @@
} }
.cm-editor { .cm-editor {
@apply w-full block text-[0.85rem]; @apply w-full block text-[0.85rem] bg-gray-25;
&.cm-focused { &.cm-focused {
outline: none !important; outline: none !important;
@@ -27,7 +27,7 @@
} }
.placeholder-widget { .placeholder-widget {
@apply text-xs text-white/90 bg-blue-400/80 py-[1px] px-1 mx-[1px] rounded cursor-default hover:bg-blue-400 hover:text-white; @apply text-xs text-white/90 bg-blue-400/80 py-[0.5px] px-1 mx-[1px] rounded cursor-default hover:bg-blue-400 hover:text-white;
text-shadow: 0 0 1px rgba(0, 0, 0, 0.9); text-shadow: 0 0 1px rgba(0, 0, 0, 0.9);
} }
} }
@@ -70,8 +70,7 @@
} }
.cm-editor .cm-gutters { .cm-editor .cm-gutters {
/*@apply bg-gray-50 border-r-0 text-gray-200;*/ @apply bg-gray-25 border-0 text-gray-200;
@apply bg-transparent border-0 text-gray-200;
} }
.cm-editor .cm-gutterElement { .cm-editor .cm-gutterElement {

View File

@@ -0,0 +1,12 @@
import classnames from 'classnames';
import type { HTMLAttributes } from 'react';
type Props = HTMLAttributes<HTMLHeadingElement>;
export function Heading({ className, children, ...props }: Props) {
return (
<h1 className={classnames(className, 'text-2xl font-semibold text-gray-900 mb-3')} {...props}>
{children}
</h1>
);
}

View File

@@ -122,13 +122,16 @@ export function ResponsePane({ requestId, className }: Props) {
))} ))}
</HStack> </HStack>
</ScrollArea> </ScrollArea>
{viewMode === 'pretty' && contentForIframe !== null ? ( {viewMode === 'pretty' && contentForIframe !== null ? (
<iframe <div className="pl-2">
title="Response preview" <iframe
srcDoc={contentForIframe} title="Response preview"
sandbox="allow-scripts allow-same-origin" srcDoc={contentForIframe}
className="h-full w-full rounded-lg" sandbox="allow-scripts allow-same-origin"
/> className="h-full w-full rounded-lg"
/>
</div>
) : response?.body ? ( ) : response?.body ? (
<Editor <Editor
valueKey={`${contentType}:${response.body}`} valueKey={`${contentType}:${response.body}`}

View File

@@ -0,0 +1,24 @@
import React from 'react';
import { Link, useRouteError } from 'react-router-dom';
import { Button } from './Button';
import { ButtonLink } from './ButtonLink';
import { Heading } from './Heading';
import { VStack } from './Stacks';
export function RouterError() {
const error = useRouteError();
console.log('Router Error', error);
return (
<div className="flex items-center justify-center h-full">
<VStack space={5} className="w-auto h-auto">
<Heading>Route Error 🔥</Heading>
<pre className="text-sm select-auto cursor-text bg-gray-50 p-3 rounded">
{JSON.stringify(error, null, 2)}
</pre>
<ButtonLink to="/" color="primary">
Go Home
</ButtonLink>
</VStack>
</div>
);
}

View File

@@ -7,10 +7,8 @@ import useTheme from '../hooks/useTheme';
import type { HttpRequest } from '../lib/models'; import type { HttpRequest } from '../lib/models';
import { Button } from './Button'; import { Button } from './Button';
import { Dialog } from './Dialog'; import { Dialog } from './Dialog';
import { DropdownMenuRadio } from './Dropdown';
import { HeaderEditor } from './HeaderEditor'; import { HeaderEditor } from './HeaderEditor';
import { IconButton } from './IconButton'; import { IconButton } from './IconButton';
import { Input } from './Input';
import { HStack, VStack } from './Stacks'; import { HStack, VStack } from './Stacks';
import { WindowDragRegion } from './WindowDragRegion'; import { WindowDragRegion } from './WindowDragRegion';
@@ -25,8 +23,8 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
const { toggleTheme } = useTheme(); const { toggleTheme } = useTheme();
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
return ( return (
<div className={classnames(className, 'w-52 bg-gray-50 h-full')} {...props}> <div className={classnames(className, 'w-52 bg-gray-50 h-full px-2')} {...props}>
<HStack as={WindowDragRegion} items="center" className="pr-1" justify="end"> <HStack as={WindowDragRegion} items="center" className="py-2" justify="end">
<Dialog wide open={open} onOpenChange={setOpen} title="Edit Headers"> <Dialog wide open={open} onOpenChange={setOpen} title="Edit Headers">
<HeaderEditor /> <HeaderEditor />
<Button className="ml-auto mt-5" color="primary" onClick={() => setOpen(false)}> <Button className="ml-auto mt-5" color="primary" onClick={() => setOpen(false)}>
@@ -60,12 +58,12 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
function SidebarItem({ request, active }: { request: HttpRequest; active: boolean }) { function SidebarItem({ request, active }: { request: HttpRequest; active: boolean }) {
return ( return (
<li key={request.id} className="mx-3"> <li key={request.id}>
<Button <Button
as={Link} as={Link}
to={`/workspaces/${request.workspaceId}/requests/${request.id}`} to={`/workspaces/${request.workspaceId}/requests/${request.id}`}
className={classnames('w-full', active && 'bg-gray-500/[0.1]')} className={classnames('w-full', active && 'bg-gray-500/[0.1] text-gray-900')}
size="xs" size="sm"
justify="start" justify="start"
> >
{request.name} {request.name}

View File

@@ -8,6 +8,8 @@ const spaceClassesX = {
2: 'pr-2', 2: 'pr-2',
3: 'pr-3', 3: 'pr-3',
4: 'pr-4', 4: 'pr-4',
5: 'pr-5',
6: 'pr-6',
}; };
const spaceClassesY = { const spaceClassesY = {
@@ -16,6 +18,8 @@ const spaceClassesY = {
2: 'pt-2', 2: 'pt-2',
3: 'pt-3', 3: 'pt-3',
4: 'pt-4', 4: 'pt-4',
5: 'pt-5',
6: 'pt-6',
}; };
interface HStackProps extends BaseStackProps { interface HStackProps extends BaseStackProps {

View File

@@ -6,8 +6,8 @@ type Props = HTMLAttributes<HTMLDivElement>;
export function WindowDragRegion({ className, ...props }: Props) { export function WindowDragRegion({ className, ...props }: Props) {
return ( return (
<div <div
className={classnames(className, 'w-full h-14 flex-shrink-0')} data-tauri-drag-region
data-tauri-drag-region="" className={classnames(className, 'w-full h-8 flex-shrink-0 box-content')}
{...props} {...props}
/> />
); );

View File

@@ -1,20 +1,21 @@
import React from 'react';
import init, { greet } from 'hello';
import ReactDOM from 'react-dom/client';
import App from './App';
import { HelmetProvider } from 'react-helmet-async';
import { MotionConfig } from 'framer-motion';
import { listen } from '@tauri-apps/api/event';
import { responsesQueryKey } from './hooks/useResponses';
import { setTheme } from './lib/theme';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { listen } from '@tauri-apps/api/event';
import { MotionConfig } from 'framer-motion';
import init, { greet } from 'hello';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { HelmetProvider } from 'react-helmet-async';
import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import App from './App';
import { Layout } from './components/Layout'; import { Layout } from './components/Layout';
import { Workspaces } from './pages/Workspaces'; import { RouterError } from './components/RouterError';
import './main.css'; import { requestsQueryKey } from './hooks/useRequest';
import { responsesQueryKey } from './hooks/useResponses';
import type { HttpRequest, HttpResponse } from './lib/models'; import type { HttpRequest, HttpResponse } from './lib/models';
import { convertDates } from './lib/models'; import { convertDates } from './lib/models';
import { requestsQueryKey } from './hooks/useRequest'; import { setTheme } from './lib/theme';
import './main.css';
import { Workspaces } from './pages/Workspaces';
setTheme(); setTheme();
@@ -78,6 +79,7 @@ const router = createBrowserRouter([
{ {
path: '/', path: '/',
element: <Layout />, element: <Layout />,
errorElement: <RouterError />,
children: [ children: [
{ {
path: '/', path: '/',

View File

@@ -1,15 +1,18 @@
import { Heading } from '../components/Heading';
import { VStack } from '../components/Stacks';
import { useWorkspaces } from '../hooks/useWorkspaces'; import { useWorkspaces } from '../hooks/useWorkspaces';
import { ButtonLink } from '../components/ButtonLink'; import { ButtonLink } from '../components/ButtonLink';
export function Workspaces() { export function Workspaces() {
const workspaces = useWorkspaces(); const workspaces = useWorkspaces();
return ( return (
<ul className="p-12"> <VStack as="ul" className="p-12">
<Heading>Workspaces</Heading>
{workspaces.data?.map((w) => ( {workspaces.data?.map((w) => (
<ButtonLink key={w.id} to={`/workspaces/${w.id}`}> <ButtonLink key={w.id} color="gray" to={`/workspaces/${w.id}`}>
{w.name} {w.name}
</ButtonLink> </ButtonLink>
))} ))}
</ul> </VStack>
); );
} }