Skip to content

Commit

Permalink
chore: First implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
daavidrgz committed Oct 9, 2024
1 parent 6f18803 commit 52a368b
Show file tree
Hide file tree
Showing 13 changed files with 355 additions and 38 deletions.
33 changes: 33 additions & 0 deletions crates/web/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/web/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@radix-ui/react-hover-card": "^1.1.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.1",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slider": "^1.2.1",
Expand Down
2 changes: 2 additions & 0 deletions crates/web/frontend/src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { memo } from "react";
import ExamplesSheet from "../examples-sheet/examples-sheet";
import ShortcutPopup from "../shortcut-popup/shortcut-popup";
import StarCount from "../star-count/star-count";
import SharePopup from "../share-popup/share-popup";

interface Props {
className?: string;
Expand All @@ -28,6 +29,7 @@ const Header = ({ className, onClickExample }: Props) => {
</h1>

<div className="flex justify-end flex-grow basis-0 gap-4">
<SharePopup />
<ThemeButton />
<SettingsSheet />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { fromString } from "@/model/http-method";
import { type LoadingState, notLoading } from "@/model/loading-state";
import { File, FileUp, Trash } from "lucide-react";
import { type ChangeEvent, useCallback, useMemo, useState } from "react";
import BodyTab from "../body-tab/body-tab";
import HeadersTab from "../headers-tab/headers-tab";
import RequestBodyTab from "../request-body-tab/request-body-tab";
import RequestHeadersTab from "../request-headers-tab/request-headers-tab";
import { Button } from "../ui/button";
import {
Dialog,
Expand Down Expand Up @@ -211,11 +211,11 @@ const ImportPopup = ({
)}
</TabsList>
<TabsContent value="headers" className="overflow-y-auto">
<HeadersTab headers={headers} setHeaders={setHeaders} />
<RequestHeadersTab headers={headers} setHeaders={setHeaders} />
</TabsContent>
{httpMethod === "POST" && (
<TabsContent value="body" className="pb-16 overflow-y-auto min-h-full">
<BodyTab body={instantBody} setBody={setBody} />
<RequestBodyTab body={instantBody} setBody={setBody} />
</TabsContent>
)}
</Tabs>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import CodeMirror, { type Extension } from "@uiw/react-codemirror";
import { useCallback, useEffect, useMemo, useState } from "react";
import { formatCode, getCodemirrorExtensionsByFileType } from "../editor/editor-utils";

interface BodyTabProps {
interface RequestBodyTabProps {
body: string;
setBody: (body: string) => void;
}

const BodyTab = ({ body, setBody }: BodyTabProps) => {
const RequestBodyTab = ({ body, setBody }: RequestBodyTabProps) => {
const [focused, setFocused] = useState(false);
const {
settings: {
Expand Down Expand Up @@ -67,4 +67,4 @@ const BodyTab = ({ body, setBody }: BodyTabProps) => {
);
};

export default BodyTab;
export default RequestBodyTab;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ interface HeadersDatalistProps {
id: string;
}

const HeadersDatalist = ({ id }: HeadersDatalistProps) => {
const RequestHeadersDatalist = ({ id }: HeadersDatalistProps) => {
return (
<datalist id={id}>
<option value="Accept" />
Expand All @@ -26,4 +26,4 @@ const HeadersDatalist = ({ id }: HeadersDatalistProps) => {
);
};

export default HeadersDatalist;
export default RequestHeadersDatalist;
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { Trash } from "lucide-react";
import { useCallback } from "react";
import { Checkbox } from "../ui/checkbox";
import { Input } from "../ui/input";
import HeadersDatalist from "./headers-datalist";
import RequestHeadersDatalist from "./request-headers-datalist";

interface HeadersTabProps {
interface RequestHeadersTabProps {
headers: [string, string, boolean][];
setHeaders: (headers: [string, string, boolean][]) => void;
}

const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
const RequestHeadersTab = ({ headers, setHeaders }: RequestHeadersTabProps) => {
const updateHeaders = useCallback(
(index: number, key: string, value: string, enabled: boolean) => {
const newHeaders: [string, string, boolean][] = headers.map((header, i) =>
Expand Down Expand Up @@ -58,7 +58,7 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
onChange={(e) => updateHeaders(index, e.target.value, header[1], header[2])}
className="w-1/2 p-2 border rounded-md mb-0 peer-data-[state=unchecked]:opacity-50 transition-opacity"
/>
<HeadersDatalist id="header-list" />
<RequestHeadersDatalist id="header-list" />
<Input
type="text"
placeholder="Value"
Expand All @@ -85,4 +85,4 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
);
};

export default HeadersTab;
export default RequestHeadersTab;
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ const SettingsSheet = ({ className }: Props) => {
<SheetDescription>Configure the playground settings to your liking.</SheetDescription>
</SheetHeader>
<Separator />
<div className="flex flex-col gap-4">
<div className="flex gap-4 items-center">
<div className="flex flex-col">
<div className="flex gap-4 items-center mb-4">
<Label htmlFor="auto-apply" className="text-md font-semibold cursor-pointer">
Auto apply
</Label>
Expand All @@ -64,28 +64,28 @@ const SettingsSheet = ({ className }: Props) => {
onCheckedChange={(checked) => setSettings((prev) => setAutoApply(prev, checked))}
/>
</div>
<div className="ml-4">
<Label
htmlFor="debounce-time"
variant={autoApplySettings.autoApply ? "default" : "disabled"}
>
Debounce time (ms)
</Label>
<Slider
id="debounce-time"
disabled={!autoApplySettings.autoApply}
onValueChange={(value) => setSettings((prev) => setDebounceTime(prev, value[0]))}
value={[autoApplySettings.debounceTime]}
max={5000}
min={0}
step={250}
/>
</div>
<Label
htmlFor="debounce-time"
variant={autoApplySettings.autoApply ? "default" : "disabled"}
className="mb-4"
>
Debounce time
</Label>
<Slider
id="debounce-time"
disabled={!autoApplySettings.autoApply}
onValueChange={(value) => setSettings((prev) => setDebounceTime(prev, value[0]))}
value={[autoApplySettings.debounceTime]}
max={5000}
min={0}
step={250}
units="ms"
/>
</div>
<Separator />
<div className="flex flex-col gap-4 w-full">
<h2 className="text-md font-semibold">Indentation</h2>
<div className="flex gap-8 ml-4">
<div className="flex gap-8">
<div className="flex flex-col gap-2">
<Label htmlFor="json-tab-size">Data indent</Label>
<Select
Expand Down
117 changes: 117 additions & 0 deletions crates/web/frontend/src/components/share-popup/share-popup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { InfoIcon, Share, X } from "lucide-react";
import { useState } from "react";
import ActionButton from "../action-button/action-button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../ui/dialog";
import { Button } from "../ui/button";
import { Label } from "../ui/label";
import { Slider } from "../ui/slider";
import { SliderWithMarks } from "../ui/slider-with-marks";
import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
import { Tooltip, TooltipProvider } from "@radix-ui/react-tooltip";
import { TooltipContent, TooltipTrigger } from "../ui/tooltip";

const SharePopup = () => {
const [open, setOpen] = useState(false);
const [expirationTime, setExpirationTime] = useState(3600);

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};

const handleExpirationTimeChange = (value: number[]) => {
setExpirationTime(value[0]);
};

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<ActionButton description="Share your playground" className="p-3 border-accent-subtle">
<Share className="w-4 h-4" />
</ActionButton>
</DialogTrigger>
<DialogContent className="w-[34rem] max-w-[80vw] max-h-[80vh] gap-0">
<X
className="absolute top-4 right-4 h-4 w-4 cursor-pointer"
onClick={() => setOpen(false)}
/>
<DialogHeader>
<DialogTitle className="flex items-center">
<span>Share your playground</span>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="w-3 h-3 ml-2 mt-1" />
</TooltipTrigger>
<TooltipContent className="w-1/2 font-normal p-4">
<span>
When generating a sharable link,{" "}
<span className="font-semibold">
the content of the input json and the query will be saved in our servers
</span>
. This data won't be accessible when the expiration time is reached.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</DialogTitle>
<DialogDescription>
Create a shareable link to your current playground state
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} autoComplete="off" className="overflow-x-auto mt-8">
<div>
<Label htmlFor="expiration-time" variant="default">
Expiration time
</Label>
<RadioGroup
className="mt-4 text-xs flex gap-4"
defaultValue="1-day"
id="expiration-time"
>
<div className="flex items-center">
<RadioGroupItem value="1-hour" id="1-hour" />
<Label className="pl-2" htmlFor="1-hour">
1 hour
</Label>
</div>
<div className="flex items-center">
<RadioGroupItem value="1-day" id="1-day" />
<Label className="pl-2" htmlFor="1-day">
1 day
</Label>
</div>
<div className="flex items-center">
<RadioGroupItem value="1-week" id="1-week" />
<Label className="pl-2" htmlFor="1-week">
1 week
</Label>
</div>
</RadioGroup>
</div>
<div className="flex justify-between mt-8">
<Button
className="py-1 px-8"
variant="outline"
onClick={() => setOpen(false)}
type="button"
>
Cancel
</Button>
<Button className="py-1 px-8" variant="success" type="submit">
Create
</Button>
</div>
</form>
</DialogContent>
</Dialog>
);
};

export default SharePopup;
38 changes: 38 additions & 0 deletions crates/web/frontend/src/components/ui/radio-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client";

import * as React from "react";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { Circle } from "lucide-react";

import { cn } from "@/lib/utils";

const RadioGroup = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
>(({ className, ...props }, ref) => {
return <RadioGroupPrimitive.Root className={cn("grid gap-2", className)} {...props} ref={ref} />;
});
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;

const RadioGroupItem = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
>(({ className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="h-2 w-2 fill-current text-current" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
);
});
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;

export { RadioGroup, RadioGroupItem };
Loading

0 comments on commit 52a368b

Please sign in to comment.