Skip to content

Commit 9ad0d96

Browse files
authored
fix UX according to diogo feedback (#33)
1 parent 15555e0 commit 9ad0d96

File tree

8 files changed

+148
-118
lines changed

8 files changed

+148
-118
lines changed

packages/app/src/components/ApiTree/ApiTree.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export default function ApiTree(props: {
9292
position: { x: 0, y: 0 }, // Initial position, will be updated by Dagre
9393
data: {
9494
path: child.path,
95+
isBeingDragged: false,
9596
},
9697
type: "groupNode",
9798
};
@@ -142,6 +143,7 @@ export default function ApiTree(props: {
142143
onChangeGroup: (group: string) =>
143144
props.onChangeEndpointGroup(group, endpoint),
144145
groupColor: color,
146+
isBeingDragged: false,
145147
},
146148
type: "endpointNode",
147149
};
@@ -197,13 +199,35 @@ export default function ApiTree(props: {
197199
}
198200
}, []);
199201

202+
function onNodeDragStart(_event: React.MouseEvent, node: Node) {
203+
setNodes((nds) =>
204+
nds.map((n) =>
205+
n.id === node.id
206+
? { ...n, data: { ...n.data, isBeingDragged: true } }
207+
: n,
208+
),
209+
);
210+
}
211+
212+
function onNodeDragStop(_event: React.MouseEvent, node: Node) {
213+
setNodes((nds) =>
214+
nds.map((n) =>
215+
n.id === node.id
216+
? { ...n, data: { ...n.data, isBeingDragged: false } }
217+
: n,
218+
),
219+
);
220+
}
221+
200222
return (
201223
<ReactFlow
202224
nodeTypes={nodeTypes}
203225
edgeTypes={edgeTypes}
204226
nodes={nodes}
205227
edges={edges}
206228
onNodesChange={onNodesChange}
229+
onNodeDragStart={onNodeDragStart}
230+
onNodeDragStop={onNodeDragStop}
207231
fitView
208232
>
209233
<div className="absolute bottom-6 inset-x-4 z-10 flex justify-around">

packages/app/src/components/ApiTree/EndpointNode.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ export default function EndpointNode(
1212
endpoint: Endpoint;
1313
onChangeGroup: (group: string) => void;
1414
groupColor?: string;
15+
isBeingDragged: boolean;
1516
}>
1617
>,
1718
) {
1819
return (
19-
<div className="bg-secondarySurface-light dark:bg-secondarySurface-dark rounded-xl border border-border-light dark:border-border-dark overflow-hidden">
20+
<div
21+
className={`bg-secondarySurface-light dark:bg-secondarySurface-dark rounded-xl border border-border-light dark:border-border-dark overflow-hidden ${props.data.isBeingDragged ? "bg-blue-100 dark:bg-blue-900 shadow-lg" : ""}`}
22+
>
2023
<Handle
2124
type="target"
2225
position={props.targetPosition || Position.Top}

packages/app/src/components/ApiTree/EndpointNodeContentDialog.tsx

+100-109
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
Separator,
66
TextField,
77
} from "@radix-ui/themes";
8-
import { FormEvent, useEffect, useState } from "react";
8+
import { FormEvent, useState } from "react";
99
import { Endpoint } from "../../service/api/types";
1010
import MethodBadge from "../MethodBadge";
1111

@@ -14,20 +14,24 @@ export default function EndpointNodeContentDialog(props: {
1414
endpoint: Endpoint;
1515
onChangeGroup: (group: string) => void;
1616
}) {
17+
const [open, setOpen] = useState<boolean>(false);
1718
const [group, setGroup] = useState<string>("");
1819

1920
async function handleApply(e: FormEvent<HTMLFormElement>) {
2021
e.preventDefault();
21-
if (group === props.endpoint.group) return;
22-
props.onChangeGroup(group);
22+
if (group !== props.endpoint.group) {
23+
props.onChangeGroup(group);
24+
}
25+
setOpen(false);
2326
}
2427

25-
useEffect(() => {
28+
function handleOpenChange(open: boolean) {
2629
setGroup(props.endpoint.group || "");
27-
}, [props.endpoint]);
30+
setOpen(open);
31+
}
2832

2933
return (
30-
<Dialog.Root>
34+
<Dialog.Root open={open} onOpenChange={handleOpenChange}>
3135
<Dialog.Trigger>
3236
<Button variant="ghost" size="1" highContrast disabled={props.busy}>
3337
<svg
@@ -63,119 +67,106 @@ export default function EndpointNodeContentDialog(props: {
6367
</div>
6468
</Dialog.Title>
6569
<Dialog.Description></Dialog.Description>
66-
<Separator
67-
orientation="horizontal"
68-
size="4"
69-
className="bg-[#FFFFFF1F]"
70-
/>
71-
<DataList.Root className="mt-5">
72-
<DataList.Item align="center">
73-
<DataList.Label className="text-gray-light dark:text-gray-dark">
74-
Method
75-
</DataList.Label>
76-
<DataList.Value>
77-
<MethodBadge method={props.endpoint.method} />
78-
</DataList.Value>
79-
</DataList.Item>
80-
<DataList.Item>
81-
<DataList.Label className="text-gray-light dark:text-gray-dark">
82-
Path
83-
</DataList.Label>
84-
<DataList.Value>
85-
<div>{props.endpoint.path}</div>
86-
</DataList.Value>
87-
</DataList.Item>
88-
<DataList.Item align="center">
89-
<DataList.Label className="text-gray-light dark:text-gray-dark">
90-
Group
91-
</DataList.Label>
92-
<DataList.Value>
93-
<form className="flex gap-2 items-center" onSubmit={handleApply}>
70+
<form onSubmit={handleApply}>
71+
<Separator
72+
orientation="horizontal"
73+
size="4"
74+
className="bg-[#FFFFFF1F]"
75+
/>
76+
<DataList.Root className="mt-5">
77+
<DataList.Item align="center">
78+
<DataList.Label className="text-gray-light dark:text-gray-dark">
79+
Method
80+
</DataList.Label>
81+
<DataList.Value>
82+
<MethodBadge method={props.endpoint.method} />
83+
</DataList.Value>
84+
</DataList.Item>
85+
<DataList.Item>
86+
<DataList.Label className="text-gray-light dark:text-gray-dark">
87+
Path
88+
</DataList.Label>
89+
<DataList.Value>
90+
<div>{props.endpoint.path}</div>
91+
</DataList.Value>
92+
</DataList.Item>
93+
<DataList.Item align="center">
94+
<DataList.Label className="text-gray-light dark:text-gray-dark">
95+
Group
96+
</DataList.Label>
97+
<DataList.Value>
9498
<TextField.Root
9599
value={group}
96100
onChange={(e) => setGroup(e.target.value)}
97101
disabled={props.busy}
98102
placeholder="Set a group name"
99103
/>
100-
<Button variant="ghost" disabled={props.busy} type="submit">
101-
<svg
102-
width="16"
103-
height="16"
104-
viewBox="0 0 16 16"
105-
xmlns="http://www.w3.org/2000/svg"
106-
className="text-gray-light dark:text-gray-dark"
107-
fill="currentColor"
108-
>
109-
<path d="M15.2353 0.765303C14.7821 0.312767 14.1678 0.0585938 13.5273 0.0585938C12.8869 0.0585938 12.2726 0.312767 11.8193 0.765303L0.976677 11.608C0.666178 11.9167 0.419985 12.284 0.252342 12.6885C0.0846994 13.093 -0.00106532 13.5268 9.98748e-06 13.9646V15.3333C9.98748e-06 15.5101 0.0702479 15.6797 0.195272 15.8047C0.320296 15.9297 0.489866 16 0.666677 16H2.03534C2.47319 16.0012 2.90692 15.9156 3.31145 15.748C3.71597 15.5805 4.08325 15.3344 4.39201 15.024L15.2353 4.18064C15.6877 3.72743 15.9417 3.11328 15.9417 2.47297C15.9417 1.83266 15.6877 1.21851 15.2353 0.765303ZM3.44934 14.0813C3.07335 14.4548 2.56532 14.6651 2.03534 14.6666H1.33334V13.9646C1.33267 13.7019 1.38411 13.4417 1.4847 13.1989C1.58529 12.9562 1.73302 12.7359 1.91934 12.5506L10.148 4.32197L11.6813 5.8553L3.44934 14.0813ZM14.292 3.23797L12.6213 4.9093L11.088 3.3793L12.7593 1.70797C12.86 1.60751 12.9795 1.52786 13.111 1.47358C13.2424 1.41929 13.3833 1.39143 13.5255 1.39158C13.6678 1.39174 13.8086 1.41991 13.9399 1.47448C14.0712 1.52905 14.1905 1.60896 14.291 1.70964C14.3915 1.81032 14.4711 1.9298 14.5254 2.06126C14.5797 2.19272 14.6076 2.33359 14.6074 2.47581C14.6072 2.61804 14.5791 2.75885 14.5245 2.89019C14.4699 3.02153 14.39 3.14084 14.2893 3.2413L14.292 3.23797Z" />
110-
</svg>
111-
</Button>
112-
{group === (props.endpoint.group || "") && (
113-
<svg
114-
width="16"
115-
height="16"
116-
viewBox="0 0 16 16"
117-
xmlns="http://www.w3.org/2000/svg"
118-
className="text-[green]"
119-
fill="currentColor"
120-
>
121-
<path d="M7.99998 1.3335C11.682 1.3335 14.6666 4.31816 14.6666 8.00016C14.6666 11.6822 11.682 14.6668 7.99998 14.6668C4.31798 14.6668 1.33331 11.6822 1.33331 8.00016C1.33331 4.31816 4.31798 1.3335 7.99998 1.3335ZM10.1466 5.98016L7.16665 8.96016L5.85331 7.64683C5.75853 7.55851 5.63317 7.51043 5.50363 7.51271C5.3741 7.515 5.25051 7.56747 5.1589 7.65908C5.06729 7.75069 5.01482 7.87428 5.01253 8.00381C5.01024 8.13335 5.05833 8.25871 5.14665 8.3535L6.81331 10.0202C6.90706 10.1138 7.03415 10.1664 7.16665 10.1664C7.29915 10.1664 7.42623 10.1138 7.51998 10.0202L10.8533 6.68683C10.9024 6.64105 10.9418 6.58585 10.9692 6.52452C10.9965 6.46319 11.0112 6.39698 11.0124 6.32984C11.0136 6.26271 11.0012 6.19602 10.9761 6.13376C10.9509 6.07151 10.9135 6.01495 10.866 5.96747C10.8185 5.91999 10.762 5.88256 10.6997 5.85741C10.6375 5.83227 10.5708 5.81992 10.5036 5.8211C10.4365 5.82229 10.3703 5.83698 10.309 5.86431C10.2476 5.89164 10.1924 5.93104 10.1466 5.98016Z" />
122-
</svg>
123-
)}
124-
</form>
125-
</DataList.Value>
126-
</DataList.Item>
127-
</DataList.Root>
104+
</DataList.Value>
105+
</DataList.Item>
106+
</DataList.Root>
107+
108+
<Separator
109+
orientation="horizontal"
110+
size="4"
111+
className="bg-[#FFFFFF1F] mt-5"
112+
/>
128113

129-
<Separator
130-
orientation="horizontal"
131-
size="4"
132-
className="bg-[#FFFFFF1F] mt-5"
133-
/>
114+
<DataList.Root className="mt-5">
115+
<DataList.Item>
116+
<DataList.Label className="text-gray-light dark:text-gray-dark">
117+
Source:
118+
</DataList.Label>
119+
<DataList.Value>
120+
<div>{props.endpoint.filePath}</div>
121+
</DataList.Value>
122+
</DataList.Item>
134123

135-
<DataList.Root className="mt-5">
136-
<DataList.Item>
137-
<DataList.Label className="text-gray-light dark:text-gray-dark">
138-
Source:
139-
</DataList.Label>
140-
<DataList.Value>
141-
<div>{props.endpoint.filePath}</div>
142-
</DataList.Value>
143-
</DataList.Item>
124+
<DataList.Item>
125+
<DataList.Label className="text-gray-light dark:text-gray-dark">
126+
Dependants:
127+
</DataList.Label>
128+
<DataList.Value>
129+
<ul className="list-disc">
130+
{props.endpoint.parentFilePaths.length > 0 ? (
131+
props.endpoint.parentFilePaths.map((parent, index) => (
132+
<li key={index}>{parent}</li>
133+
))
134+
) : (
135+
<li>No dependants</li>
136+
)}
137+
</ul>
138+
</DataList.Value>
139+
</DataList.Item>
144140

145-
<DataList.Item>
146-
<DataList.Label className="text-gray-light dark:text-gray-dark">
147-
Dependants:
148-
</DataList.Label>
149-
<DataList.Value>
150-
<ul className="list-disc">
151-
{props.endpoint.parentFilePaths.length > 0 ? (
152-
props.endpoint.parentFilePaths.map((parent, index) => (
153-
<li key={index}>{parent}</li>
154-
))
155-
) : (
156-
<li>No dependants</li>
157-
)}
158-
</ul>
159-
</DataList.Value>
160-
</DataList.Item>
141+
<DataList.Item>
142+
<DataList.Label className="text-gray-light dark:text-gray-dark">
143+
Dependencies:
144+
</DataList.Label>
145+
<DataList.Value>
146+
<ul className="list-disc">
147+
{props.endpoint.childrenFilePaths.length > 0 ? (
148+
props.endpoint.childrenFilePaths.map((child, index) => (
149+
<li key={index}>{child}</li>
150+
))
151+
) : (
152+
<li>No dependencies</li>
153+
)}
154+
</ul>
155+
</DataList.Value>
156+
</DataList.Item>
157+
</DataList.Root>
161158

162-
<DataList.Item>
163-
<DataList.Label className="text-gray-light dark:text-gray-dark">
164-
Dependencies:
165-
</DataList.Label>
166-
<DataList.Value>
167-
<ul className="list-disc">
168-
{props.endpoint.childrenFilePaths.length > 0 ? (
169-
props.endpoint.childrenFilePaths.map((child, index) => (
170-
<li key={index}>{child}</li>
171-
))
172-
) : (
173-
<li>No dependencies</li>
174-
)}
175-
</ul>
176-
</DataList.Value>
177-
</DataList.Item>
178-
</DataList.Root>
159+
<div className="flex justify-end gap-5 mt-5">
160+
<Dialog.Close>
161+
<Button disabled={props.busy} color="gray">
162+
Cancel
163+
</Button>
164+
</Dialog.Close>
165+
<Button type="submit" disabled={props.busy} color="blue">
166+
Apply
167+
</Button>
168+
</div>
169+
</form>
179170
</Dialog.Content>
180171
</Dialog.Root>
181172
);

packages/app/src/components/ApiTree/GroupNode.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { Handle, Node, NodeProps, Position } from "@xyflow/react";
22

3-
export default function GroupNode(props: NodeProps<Node<{ path: string }>>) {
3+
export default function GroupNode(
4+
props: NodeProps<Node<{ path: string; isBeingDragged: boolean }>>,
5+
) {
46
return (
5-
<div className="bg-secondarySurface-light dark:bg-secondarySurface-dark rounded-xl border border-border-light dark:border-border-dark">
7+
<div
8+
className={`bg-secondarySurface-light dark:bg-secondarySurface-dark rounded-xl border border-border-light dark:border-border-dark ${props.data.isBeingDragged ? "bg-blue-100 dark:bg-blue-900 shadow-lg" : ""}`}
9+
>
610
<Handle
711
type="target"
812
position={props.targetPosition || Position.Top}

packages/app/src/layout/default.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ export default function DefaultLayout({
1111

1212
return (
1313
<div className="flex flex-col min-h-screen bg-background-light text-text-light dark:bg-background-dark dark:text-text-dark">
14-
<div className="px-5 flex items-center gap-10">
14+
<div className="px-5 flex items-center gap-10 py-2">
1515
<a
1616
className="flex items-center gap-1 text-gray-light dark:text-gray-dark no-underline "
1717
href="https://nanoapi.io"
1818
target="_blank"
1919
>
20-
<img src="/logo.png" alt="logo" className="w-20 h-20" />
21-
<span className="text-3xl font-bold">NanoAPI</span>
20+
<img src="/logo.png" alt="logo" className="w-8 h-8" />
21+
<span className="text-xl font-bold">NanoAPI</span>
2222
</a>
2323
<a
2424
href="https://nanoapi.io/docs"

packages/app/src/pages/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export default function App() {
118118
<div className="flex flex-col">
119119
<div
120120
className="bg-secondaryBackground-light dark:bg-secondaryBackground-dark rounded-3xl overflow-hidden"
121-
style={{ height: "calc(100vh - 100px)", width: "100hh" }}
121+
style={{ height: "calc(100vh - 60px)", width: "100hh" }}
122122
>
123123
{chartLoading ? (
124124
<div className="h-full flex flex-col justify-center items-center gap-5">

packages/app/src/service/dagree.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@ import dagre from "@dagrejs/dagre";
44
function getNodeHeight(node: Node, isHorizontal: boolean) {
55
if (isHorizontal) {
66
if (node.type === "groupNode") {
7-
return 100;
7+
return 80;
88
}
99
if (node.type === "endpointNode") {
10-
return 200;
10+
return 150;
1111
}
1212
}
13+
14+
if (node.type === "groupNode") {
15+
return 80;
16+
} else if (node.type === "endpointNode") {
17+
return 150;
18+
}
19+
1320
return 100;
1421
}
1522

packages/cli/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44

55
Use indexes when editing file instead of search/replace
6+
Some UX changes
67

78
## [0.0.16] - 2024-10-30
89

0 commit comments

Comments
 (0)