Skip to content

Commit be731e7

Browse files
authored
Merge pull request #25 from VictoriaMetrics/resizable-components
main UI components become resizable
2 parents 5f576ab + 8c33fa0 commit be731e7

4 files changed

Lines changed: 114 additions & 35 deletions

File tree

cmd/sql-to-logsql/web/ui/src/components/docs/Docs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {Badge} from "@/components/ui/badge.tsx";
77

88
export function Docs() {
99
return (
10-
<Card className={"w-full min-w-[20rem] max-lg:hidden"}>
10+
<Card className={"w-full max-h-full min-w-[20rem] overflow-y-scroll"}>
1111
<CardHeader>
1212
<CardTitle className={"flex flex-row gap-2 items-center"}>
1313
<span>Information about SQL to LogsQL</span>
@@ -112,4 +112,4 @@ export function Docs() {
112112
</CardContent>
113113
</Card>
114114
)
115-
}
115+
}

cmd/sql-to-logsql/web/ui/src/components/sql-editor/SQLEditor.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ import {COMPLETIONS} from "@/components/sql-editor/complections.ts";
1616
import {CircleXIcon, CircleCheckBigIcon, PlayIcon, ListFilterIcon} from "lucide-react"
1717
import {Spinner} from "@/components/ui/spinner.tsx";
1818
import {Badge} from "@/components/ui/badge.tsx";
19+
import {cn} from "@/lib/utils";
1920

2021
export interface SqlEditorProps {
2122
readonly onRun?: (sql: string) => void;
2223
readonly isLoading?: boolean;
2324
readonly error?: string;
2425
readonly success?: string;
25-
readonly limit?: number
26+
readonly limit?: number;
27+
readonly className?: string;
2628
}
2729

2830
export function SQLEditor({
@@ -31,6 +33,7 @@ export function SQLEditor({
3133
error,
3234
success,
3335
limit,
36+
className,
3437
}: SqlEditorProps) {
3538
const [value, setValue] = useState<string>(DEFAULT_EXAMPLE_ID);
3639
const [sql, setSql] = useState("");
@@ -51,7 +54,7 @@ export function SQLEditor({
5154
}, [value]);
5255

5356
return (
54-
<Card>
57+
<Card className={cn("w-full h-full", className)}>
5558
<CardHeader className={"max-sm:flex max-sm:flex-col max-sm:gap-4 max-sm:px-4"}>
5659
<CardTitle className={"sm:py-3"}>SQL</CardTitle>
5760
<CardAction className={"flex max-sm:flex-col gap-2 w-full"}>
@@ -77,14 +80,13 @@ export function SQLEditor({
7780
</Button>
7881
</CardAction>
7982
</CardHeader>
80-
<CardContent>
83+
<CardContent className="flex-1 min-h-0">
8184
<Editor
82-
className={
83-
isLoading
84-
? "pointer-events-none opacity-50 select-none grayscale-50"
85-
: ""
86-
}
87-
height={200}
85+
className={cn(
86+
"h-full",
87+
isLoading ? "pointer-events-none opacity-50 select-none grayscale-50" : ""
88+
)}
89+
height="100%"
8890
defaultLanguage="sql"
8991
theme="vs-light"
9092
value={sql}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as React from "react"
2+
import { GripVerticalIcon } from "lucide-react"
3+
import * as ResizablePrimitive from "react-resizable-panels"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
function ResizablePanelGroup({
8+
className,
9+
...props
10+
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) {
11+
return (
12+
<ResizablePrimitive.PanelGroup
13+
data-slot="resizable-panel-group"
14+
className={cn(
15+
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
16+
className
17+
)}
18+
{...props}
19+
/>
20+
)
21+
}
22+
23+
function ResizablePanel({
24+
...props
25+
}: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
26+
return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
27+
}
28+
29+
function ResizableHandle({
30+
withHandle,
31+
className,
32+
...props
33+
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
34+
withHandle?: boolean
35+
}) {
36+
return (
37+
<ResizablePrimitive.PanelResizeHandle
38+
data-slot="resizable-handle"
39+
className={cn(
40+
"bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90",
41+
className
42+
)}
43+
{...props}
44+
>
45+
{withHandle && (
46+
<div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border">
47+
<GripVerticalIcon className="size-2.5" />
48+
</div>
49+
)}
50+
</ResizablePrimitive.PanelResizeHandle>
51+
)
52+
}
53+
54+
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }

cmd/sql-to-logsql/web/ui/src/pages/main/Main.tsx

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {useCallback, useEffect, useState} from "react";
44
import { QueryResults } from "@/components/query-results";
55
import {toast} from "sonner";
66
import {Docs} from "@/components/docs";
7+
import {ResizableHandle, ResizablePanel, ResizablePanelGroup} from "@/components/ui/resizable";
78

89
const formatExecutionTime = (ms: number): string => {
910
if (!Number.isFinite(ms) || ms < 0) {
@@ -87,30 +88,52 @@ export function Main() {
8788
}, [bearerToken, endpointUrl, endpointReadOnly, endpointEnabled]);
8889

8990
return (
90-
<main className={"p-4 w-full flex flex-col gap-4"}>
91-
<div className={"w-full flex gap-4"}>
92-
<div className={"w-full flex flex-col gap-4 min-w-[20rem] max-w-[64rem]"}>
93-
<LogsEndpoint
94-
endpointUrl={endpointUrl}
95-
onUrlChange={setEndpointUrl}
96-
bearerToken={bearerToken}
97-
onTokenChange={setBearerToken}
98-
isLoading={loading}
99-
endpointReadOnly={endpointReadOnly}
100-
endpointEnabled={endpointEnabled}
101-
onEndpointEnabledChange={setEndpointEnabled}
102-
/>
103-
<SQLEditor
104-
onRun={handleExecute}
105-
isLoading={loading}
106-
error={error}
107-
success={success}
108-
limit={limit}
109-
/>
110-
</div>
111-
<Docs />
112-
</div>
113-
<QueryResults query={query} data={results} isLoading={loading} endpointEnabled={endpointEnabled} />
91+
<main className={"p-4 w-full min-h-screen flex flex-col gap-4"}>
92+
<ResizablePanelGroup direction="vertical" className="flex-1 min-h-0">
93+
<ResizablePanel defaultSize={60} minSize={40}>
94+
<ResizablePanelGroup direction="horizontal" className="h-full min-h-0">
95+
<ResizablePanel defaultSize={60} minSize={45} className="flex flex-col min-h-0">
96+
<div className="flex h-full w-full flex-col gap-4 min-h-0 min-w-[20rem]">
97+
<div className="shrink-0">
98+
<LogsEndpoint
99+
endpointUrl={endpointUrl}
100+
onUrlChange={setEndpointUrl}
101+
bearerToken={bearerToken}
102+
onTokenChange={setBearerToken}
103+
isLoading={loading}
104+
endpointReadOnly={endpointReadOnly}
105+
endpointEnabled={endpointEnabled}
106+
onEndpointEnabledChange={setEndpointEnabled}
107+
/>
108+
</div>
109+
<SQLEditor
110+
onRun={handleExecute}
111+
isLoading={loading}
112+
error={error}
113+
success={success}
114+
limit={limit}
115+
className="flex-1 min-h-0"
116+
/>
117+
</div>
118+
</ResizablePanel>
119+
<ResizableHandle withHandle className={"m-2"} />
120+
<ResizablePanel defaultSize={30} minSize={20} className="flex">
121+
<Docs />
122+
</ResizablePanel>
123+
</ResizablePanelGroup>
124+
</ResizablePanel>
125+
<ResizableHandle withHandle className={"m-2"} />
126+
<ResizablePanel defaultSize={40} minSize={10} className="min-h-0">
127+
<div className="h-full overflow-auto">
128+
<QueryResults
129+
query={query}
130+
data={results}
131+
isLoading={loading}
132+
endpointEnabled={endpointEnabled}
133+
/>
134+
</div>
135+
</ResizablePanel>
136+
</ResizablePanelGroup>
114137
</main>
115138
);
116139
}

0 commit comments

Comments
 (0)