Skip to content

Commit 30f9e87

Browse files
committed
refactor(panels): split add tab components
Generated-By: PostHog Code Task-Id: b23f944d-dd7f-465b-9fed-1a1d028f35e1
1 parent 8f8d552 commit 30f9e87

3 files changed

Lines changed: 124 additions & 120 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { Globe, Plus, Terminal } from "@phosphor-icons/react";
2+
import {
3+
DropdownMenu,
4+
DropdownMenuContent,
5+
DropdownMenuItem,
6+
DropdownMenuTrigger,
7+
} from "@posthog/quill";
8+
import type { AddableTabKind } from "@posthog/ui/features/panels/tabAvailability";
9+
import { Tooltip } from "@posthog/ui/primitives/Tooltip";
10+
import { TabBarButton } from "./TabBarButton";
11+
12+
interface AddTabControlProps {
13+
addableTabKinds: readonly AddableTabKind[];
14+
onAddTab: (kind: AddableTabKind) => void;
15+
}
16+
17+
export function AddTabControl({
18+
addableTabKinds,
19+
onAddTab,
20+
}: AddTabControlProps) {
21+
const singleAddableTabKind =
22+
addableTabKinds.length === 1 ? addableTabKinds[0] : undefined;
23+
24+
if (singleAddableTabKind) {
25+
const isTerminal = singleAddableTabKind === "terminal";
26+
return (
27+
<Tooltip
28+
content={isTerminal ? "New terminal" : "New browser tab"}
29+
side="bottom"
30+
>
31+
<TabBarButton
32+
ariaLabel={isTerminal ? "Add terminal" : "Add browser tab"}
33+
dataAttr={isTerminal ? "panel-add-terminal" : "panel-add-browser-tab"}
34+
onClick={() => onAddTab(singleAddableTabKind)}
35+
>
36+
{isTerminal ? <Plus size={14} /> : <Globe size={14} />}
37+
</TabBarButton>
38+
</Tooltip>
39+
);
40+
}
41+
42+
if (addableTabKinds.length === 0) return null;
43+
44+
const canAddTerminal = addableTabKinds.includes("terminal");
45+
const canAddBrowser = addableTabKinds.includes("browser");
46+
47+
return (
48+
<DropdownMenu>
49+
<DropdownMenuTrigger
50+
render={
51+
<TabBarButton ariaLabel="Add tab" dataAttr="panel-add-tab">
52+
<Plus size={14} />
53+
</TabBarButton>
54+
}
55+
/>
56+
<DropdownMenuContent
57+
align="start"
58+
side="bottom"
59+
sideOffset={4}
60+
className="min-w-[140px]"
61+
>
62+
{canAddTerminal && (
63+
<DropdownMenuItem
64+
data-attr="panel-add-terminal"
65+
onClick={() => onAddTab("terminal")}
66+
>
67+
<Terminal size={14} />
68+
Terminal
69+
</DropdownMenuItem>
70+
)}
71+
{canAddBrowser && (
72+
<DropdownMenuItem
73+
data-attr="panel-add-browser-tab"
74+
onClick={() => onAddTab("browser")}
75+
>
76+
<Globe size={14} />
77+
Browser
78+
</DropdownMenuItem>
79+
)}
80+
</DropdownMenuContent>
81+
</DropdownMenu>
82+
);
83+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type React from "react";
2+
import { forwardRef, useState } from "react";
3+
4+
interface TabBarButtonProps {
5+
ariaLabel: string;
6+
dataAttr?: string;
7+
onClick?: () => void;
8+
children: React.ReactNode;
9+
}
10+
11+
export const TabBarButton = forwardRef<HTMLButtonElement, TabBarButtonProps>(
12+
function TabBarButton(
13+
{ ariaLabel, dataAttr, onClick, children, ...props },
14+
ref,
15+
) {
16+
const [isHovered, setIsHovered] = useState(false);
17+
18+
return (
19+
<button
20+
ref={ref}
21+
type="button"
22+
aria-label={ariaLabel}
23+
data-attr={dataAttr}
24+
onClick={onClick}
25+
onMouseEnter={() => setIsHovered(true)}
26+
onMouseLeave={() => setIsHovered(false)}
27+
style={{
28+
background: isHovered ? "var(--gray-4)" : "var(--color-background)",
29+
}}
30+
{...props}
31+
className="flex h-[32px] w-[32px] cursor-pointer items-center justify-center border-0 border-b border-b-(--gray-6) text-(--gray-11)"
32+
>
33+
{children}
34+
</button>
35+
);
36+
},
37+
);

packages/ui/src/features/panels/components/TabbedPanel.tsx

Lines changed: 4 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,17 @@
11
import { useDroppable } from "@dnd-kit/react";
2-
import {
3-
Globe,
4-
Plus,
5-
SquareSplitHorizontalIcon,
6-
Terminal,
7-
} from "@phosphor-icons/react";
2+
import { SquareSplitHorizontalIcon } from "@phosphor-icons/react";
83
import { useHostTRPCClient } from "@posthog/host-router/react";
9-
import {
10-
DropdownMenu,
11-
DropdownMenuContent,
12-
DropdownMenuItem,
13-
DropdownMenuTrigger,
14-
} from "@posthog/quill";
154
import { PanelDropZones } from "@posthog/ui/features/panels/components/PanelDropZones";
165
import type { SplitDirection } from "@posthog/ui/features/panels/panelLayoutStore";
176
import type { PanelContent } from "@posthog/ui/features/panels/panelTypes";
187
import type { AddableTabKind } from "@posthog/ui/features/panels/tabAvailability";
198
import { Tooltip } from "@posthog/ui/primitives/Tooltip";
209
import { Box, Flex } from "@radix-ui/themes";
2110
import type React from "react";
22-
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
11+
import { useCallback, useEffect, useRef } from "react";
12+
import { AddTabControl } from "./AddTabControl";
2313
import { PanelTab } from "./PanelTab";
14+
import { TabBarButton } from "./TabBarButton";
2415

2516
const activeTabStyle: React.CSSProperties = {
2617
height: "100%",
@@ -36,113 +27,6 @@ const hiddenTabStyle: React.CSSProperties = {
3627
pointerEvents: "none",
3728
};
3829

39-
interface TabBarButtonProps {
40-
ariaLabel: string;
41-
dataAttr?: string;
42-
// Optional so the button can serve as a DropdownMenuTrigger render target,
43-
// where the trigger injects its own click handling.
44-
onClick?: () => void;
45-
children: React.ReactNode;
46-
}
47-
48-
const TabBarButton = forwardRef<HTMLButtonElement, TabBarButtonProps>(
49-
function TabBarButton(
50-
{ ariaLabel, dataAttr, onClick, children, ...props },
51-
ref,
52-
) {
53-
const [isHovered, setIsHovered] = useState(false);
54-
55-
return (
56-
<button
57-
ref={ref}
58-
type="button"
59-
aria-label={ariaLabel}
60-
data-attr={dataAttr}
61-
onClick={onClick}
62-
onMouseEnter={() => setIsHovered(true)}
63-
onMouseLeave={() => setIsHovered(false)}
64-
style={{
65-
background: isHovered ? "var(--gray-4)" : "var(--color-background)",
66-
}}
67-
{...props}
68-
className="flex h-[32px] w-[32px] cursor-pointer items-center justify-center border-0 border-b border-b-(--gray-6) text-(--gray-11)"
69-
>
70-
{children}
71-
</button>
72-
);
73-
},
74-
);
75-
76-
interface AddTabControlProps {
77-
addableTabKinds: readonly AddableTabKind[];
78-
onAddTab: (kind: AddableTabKind) => void;
79-
}
80-
81-
function AddTabControl({ addableTabKinds, onAddTab }: AddTabControlProps) {
82-
const singleAddableTabKind =
83-
addableTabKinds.length === 1 ? addableTabKinds[0] : undefined;
84-
85-
if (singleAddableTabKind) {
86-
const isTerminal = singleAddableTabKind === "terminal";
87-
return (
88-
<Tooltip
89-
content={isTerminal ? "New terminal" : "New browser tab"}
90-
side="bottom"
91-
>
92-
<TabBarButton
93-
ariaLabel={isTerminal ? "Add terminal" : "Add browser tab"}
94-
dataAttr={isTerminal ? "panel-add-terminal" : "panel-add-browser-tab"}
95-
onClick={() => onAddTab(singleAddableTabKind)}
96-
>
97-
{isTerminal ? <Plus size={14} /> : <Globe size={14} />}
98-
</TabBarButton>
99-
</Tooltip>
100-
);
101-
}
102-
103-
if (addableTabKinds.length === 0) return null;
104-
105-
const canAddTerminal = addableTabKinds.includes("terminal");
106-
const canAddBrowser = addableTabKinds.includes("browser");
107-
108-
return (
109-
<DropdownMenu>
110-
<DropdownMenuTrigger
111-
render={
112-
<TabBarButton ariaLabel="Add tab" dataAttr="panel-add-tab">
113-
<Plus size={14} />
114-
</TabBarButton>
115-
}
116-
/>
117-
<DropdownMenuContent
118-
align="start"
119-
side="bottom"
120-
sideOffset={4}
121-
className="min-w-[140px]"
122-
>
123-
{canAddTerminal && (
124-
<DropdownMenuItem
125-
data-attr="panel-add-terminal"
126-
onClick={() => onAddTab("terminal")}
127-
>
128-
<Terminal size={14} />
129-
Terminal
130-
</DropdownMenuItem>
131-
)}
132-
{canAddBrowser && (
133-
<DropdownMenuItem
134-
data-attr="panel-add-browser-tab"
135-
onClick={() => onAddTab("browser")}
136-
>
137-
<Globe size={14} />
138-
Browser
139-
</DropdownMenuItem>
140-
)}
141-
</DropdownMenuContent>
142-
</DropdownMenu>
143-
);
144-
}
145-
14630
interface TabbedPanelProps {
14731
panelId: string;
14832
content: PanelContent;

0 commit comments

Comments
 (0)