Skip to content

Commit 8d6254e

Browse files
authored
fix mobile sizing (#11)
* fix sizing of welcome node * fix mobile sizing * fix edges on mobile * mobile properties
1 parent 9efa6db commit 8d6254e

File tree

11 files changed

+277
-22
lines changed

11 files changed

+277
-22
lines changed

app/globals.css

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@
119119
body {
120120
@apply bg-background text-foreground;
121121
}
122+
html,
123+
body {
124+
@apply overflow-hidden;
125+
}
122126
}
123127

124128
/* This layer is by next-forge */
@@ -207,12 +211,16 @@
207211
z-index: 20 !important;
208212
}
209213

210-
/* Make handles bigger on mobile for easier interaction */
214+
/* Increase hit area on mobile without changing visual size */
211215
@media (max-width: 768px) {
212-
.react-flow__handle {
213-
width: 20px !important;
214-
height: 20px !important;
215-
border: 3px solid var(--border) !important;
216+
.react-flow__handle::after {
217+
content: "" !important;
218+
position: absolute !important;
219+
top: 50% !important;
220+
left: 50% !important;
221+
transform: translate(-50%, -50%) !important;
222+
width: 44px !important;
223+
height: 44px !important;
216224
}
217225
}
218226

@@ -223,3 +231,18 @@
223231
.react-flow__handle-right {
224232
right: -0.5px !important;
225233
}
234+
235+
/* Safe area support for mobile devices */
236+
@supports (height: 100dvh) {
237+
html,
238+
body {
239+
height: 100dvh;
240+
}
241+
}
242+
243+
/* Safe area support for mobile devices */
244+
@media (max-width: 768px) {
245+
.workflow-controls-panel {
246+
bottom: calc(env(safe-area-inset-bottom, 0px) + 5.5rem) !important;
247+
}
248+
}

app/layout.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Metadata } from "next";
1+
import type { Metadata, Viewport } from "next";
22
import "./globals.css";
33
import { Provider } from "jotai";
44
import type { ReactNode } from "react";
@@ -14,6 +14,14 @@ export const metadata: Metadata = {
1414
"Build powerful workflow automations with a visual, node-based editor. Similar to n8n, built with Next.js and React Flow.",
1515
};
1616

17+
export const viewport: Viewport = {
18+
width: "device-width",
19+
initialScale: 1,
20+
maximumScale: 1,
21+
userScalable: false,
22+
viewportFit: "cover",
23+
};
24+
1725
type RootLayoutProps = {
1826
children: ReactNode;
1927
};

app/workflows/[workflowId]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ const WorkflowEditor = ({ params }: WorkflowPageProps) => {
425425
);
426426

427427
return (
428-
<div className="flex h-screen w-full flex-col overflow-hidden">
428+
<div className="flex h-dvh w-full flex-col overflow-hidden">
429429
<main className="relative flex size-full overflow-hidden">
430430
<ReactFlowProvider>
431431
{isMobile ? (

components/ai-elements/canvas.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ export const Canvas = ({ children, ...props }: CanvasProps) => {
1212
deleteKeyCode={["Backspace", "Delete"]}
1313
fitView
1414
panActivationKeyCode={null}
15-
panOnDrag
15+
panOnDrag={[1, 2]}
1616
panOnScroll
1717
selectionOnDrag={false}
1818
zoomOnDoubleClick={false}
19+
zoomOnPinch
1920
{...props}
2021
>
2122
<Background

components/ui/sheet.tsx

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import * as SheetPrimitive from "@radix-ui/react-dialog"
5+
import { XIcon } from "lucide-react"
6+
7+
import { cn } from "@/lib/utils"
8+
9+
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
10+
return <SheetPrimitive.Root data-slot="sheet" {...props} />
11+
}
12+
13+
function SheetTrigger({
14+
...props
15+
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
16+
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
17+
}
18+
19+
function SheetClose({
20+
...props
21+
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
22+
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
23+
}
24+
25+
function SheetPortal({
26+
...props
27+
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
28+
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
29+
}
30+
31+
function SheetOverlay({
32+
className,
33+
...props
34+
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
35+
return (
36+
<SheetPrimitive.Overlay
37+
data-slot="sheet-overlay"
38+
className={cn(
39+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
40+
className
41+
)}
42+
{...props}
43+
/>
44+
)
45+
}
46+
47+
function SheetContent({
48+
className,
49+
children,
50+
side = "right",
51+
...props
52+
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
53+
side?: "top" | "right" | "bottom" | "left"
54+
}) {
55+
return (
56+
<SheetPortal>
57+
<SheetOverlay />
58+
<SheetPrimitive.Content
59+
data-slot="sheet-content"
60+
className={cn(
61+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
62+
side === "right" &&
63+
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
64+
side === "left" &&
65+
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
66+
side === "top" &&
67+
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
68+
side === "bottom" &&
69+
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
70+
className
71+
)}
72+
{...props}
73+
>
74+
{children}
75+
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
76+
<XIcon className="size-4" />
77+
<span className="sr-only">Close</span>
78+
</SheetPrimitive.Close>
79+
</SheetPrimitive.Content>
80+
</SheetPortal>
81+
)
82+
}
83+
84+
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
85+
return (
86+
<div
87+
data-slot="sheet-header"
88+
className={cn("flex flex-col gap-1.5 p-4", className)}
89+
{...props}
90+
/>
91+
)
92+
}
93+
94+
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
95+
return (
96+
<div
97+
data-slot="sheet-footer"
98+
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
99+
{...props}
100+
/>
101+
)
102+
}
103+
104+
function SheetTitle({
105+
className,
106+
...props
107+
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
108+
return (
109+
<SheetPrimitive.Title
110+
data-slot="sheet-title"
111+
className={cn("text-foreground font-semibold", className)}
112+
{...props}
113+
/>
114+
)
115+
}
116+
117+
function SheetDescription({
118+
className,
119+
...props
120+
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
121+
return (
122+
<SheetPrimitive.Description
123+
data-slot="sheet-description"
124+
className={cn("text-muted-foreground text-sm", className)}
125+
{...props}
126+
/>
127+
)
128+
}
129+
130+
export {
131+
Sheet,
132+
SheetTrigger,
133+
SheetClose,
134+
SheetContent,
135+
SheetHeader,
136+
SheetFooter,
137+
SheetTitle,
138+
SheetDescription,
139+
}

components/workflow/node-config-panel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ const MultiSelectionPanel = ({
131131
};
132132

133133
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Complex UI logic with multiple conditions
134-
const PanelInner = () => {
134+
export const PanelInner = () => {
135135
const [selectedNodeId] = useAtom(selectedNodeAtom);
136136
const [selectedEdgeId] = useAtom(selectedEdgeAtom);
137137
const [nodes] = useAtom(nodesAtom);

components/workflow/nodes/add-node.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,8 @@ export function AddNode({ data }: NodeProps & { data?: AddNodeData }) {
5252
</a>
5353
</p>
5454
</div>
55-
<Button
56-
className="h-11 gap-2 text-lg shadow-lg"
57-
onClick={data.onClick}
58-
size="lg"
59-
>
60-
<Plus className="size-5.5" />
55+
<Button className="gap-2 shadow-lg" onClick={data.onClick} size="default">
56+
<Plus className="size-4" />
6157
Add a Step
6258
</Button>
6359
</div>

components/workflow/workflow-canvas.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export function WorkflowCanvas(_props: WorkflowCanvasProps) {
100100
if (!currentWorkflowId) {
101101
setViewportReady(true);
102102
setDefaultViewport(undefined);
103-
setShouldFitView(true); // Enable fitView for landing page to center the trigger node
103+
setShouldFitView(true);
104104
viewportInitialized.current = true;
105105
return;
106106
}
@@ -401,6 +401,11 @@ export function WorkflowCanvas(_props: WorkflowCanvasProps) {
401401
edgeTypes={edgeTypes}
402402
elementsSelectable={!isGenerating}
403403
fitView={shouldFitView}
404+
fitViewOptions={{
405+
maxZoom: 1,
406+
minZoom: 0.5,
407+
padding: 0.2,
408+
}}
404409
isValidConnection={isValidConnection}
405410
nodes={nodes}
406411
nodesConnectable={!isGenerating}
@@ -417,17 +422,13 @@ export function WorkflowCanvas(_props: WorkflowCanvasProps) {
417422
onSelectionChange={isGenerating ? undefined : onSelectionChange}
418423
>
419424
<Panel
420-
className="border-none bg-transparent p-0"
425+
className="workflow-controls-panel border-none bg-transparent p-0"
421426
position="bottom-left"
422427
>
423428
<Controls />
424429
</Panel>
425430
{showMinimap && (
426-
<MiniMap
427-
bgColor="var(--sidebar)"
428-
className="hidden md:flex"
429-
nodeStrokeColor="var(--border)"
430-
/>
431+
<MiniMap bgColor="var(--sidebar)" nodeStrokeColor="var(--border)" />
431432
)}
432433
</Canvas>
433434
</div>

0 commit comments

Comments
 (0)