Skip to content

Commit 707e60e

Browse files
committed
WIP: Refactor agent logs and tabs to be reusable
1 parent 75870c2 commit 707e60e

File tree

7 files changed

+507
-340
lines changed

7 files changed

+507
-340
lines changed

site/src/components/Tabs/Tabs.stories.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@ export const Default: Story = {
1313
args: {
1414
children: (
1515
<>
16-
<TabLink to="">Tab 1</TabLink>
17-
<TabLink to="tab-3">Tab 2</TabLink>
18-
<TabLink to="tab-4">Tab 3</TabLink>
16+
<TabLink value="tab-1" to="">
17+
Tab 1
18+
</TabLink>
19+
<TabLink value="tab-2" to="tab-3">
20+
Tab 2
21+
</TabLink>
22+
<TabLink value="tab-3" to="tab-4">
23+
Tab 3
24+
</TabLink>
1925
</>
2026
),
2127
},

site/src/components/Tabs/Tabs.tsx

Lines changed: 78 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,92 @@
1-
import { cx } from "@emotion/css";
2-
import { type FC, type PropsWithChildren } from "react";
3-
import { NavLink, NavLinkProps } from "react-router-dom";
4-
import { Margins } from "components/Margins/Margins";
5-
import { type ClassName, useClassName } from "hooks/useClassName";
1+
import { HTMLAttributes, type FC, createContext, useContext } from "react";
2+
import { Link, LinkProps } from "react-router-dom";
3+
import { Interpolation, Theme, useTheme } from "@emotion/react";
4+
5+
export const TAB_PADDING_Y = 12;
6+
7+
type TabsContextValue = {
8+
active: string;
9+
};
10+
11+
const TabsContext = createContext<TabsContextValue | undefined>(undefined);
12+
13+
type TabsProps = HTMLAttributes<HTMLDivElement> & TabsContextValue;
14+
15+
export const Tabs: FC<TabsProps> = ({ active, ...htmlProps }) => {
16+
const theme = useTheme();
617

7-
export const Tabs: FC<PropsWithChildren> = ({ children }) => {
818
return (
9-
<div
10-
css={(theme) => ({
11-
borderBottom: `1px solid ${theme.palette.divider}`,
12-
marginBottom: 40,
13-
})}
14-
>
15-
<Margins
19+
<TabsContext.Provider value={{ active }}>
20+
<div
1621
css={{
17-
display: "flex",
18-
alignItems: "center",
19-
gap: 2,
22+
borderBottom: `1px solid ${theme.palette.divider}`,
2023
}}
21-
>
22-
{children}
23-
</Margins>
24-
</div>
24+
{...htmlProps}
25+
/>
26+
</TabsContext.Provider>
27+
);
28+
};
29+
30+
type TabsListProps = HTMLAttributes<HTMLDivElement>;
31+
32+
export const TabsList: FC<TabsListProps> = (props) => {
33+
return (
34+
<div
35+
role="tablist"
36+
css={{
37+
display: "flex",
38+
alignItems: "baseline",
39+
}}
40+
{...props}
41+
/>
2542
);
2643
};
2744

28-
interface TabLinkProps extends NavLinkProps {
29-
className?: string;
30-
}
45+
type TabLinkProps = LinkProps & {
46+
value: string;
47+
};
3148

32-
export const TabLink: FC<TabLinkProps> = ({
33-
className,
34-
children,
35-
...linkProps
36-
}) => {
37-
const tabLink = useClassName(classNames.tabLink, []);
38-
const activeTabLink = useClassName(classNames.activeTabLink, []);
49+
export const TabLink: FC<TabLinkProps> = ({ value, ...linkProps }) => {
50+
const tabsContext = useContext(TabsContext);
51+
52+
if (!tabsContext) {
53+
throw new Error("Tab only can be used inside of Tabs");
54+
}
55+
56+
const isActive = tabsContext.active === value;
3957

4058
return (
41-
<NavLink
42-
className={({ isActive }) =>
43-
cx([tabLink, isActive && activeTabLink, className])
44-
}
59+
<Link
4560
{...linkProps}
46-
>
47-
{children}
48-
</NavLink>
61+
css={[styles.tabLink, isActive ? styles.activeTabLink : ""]}
62+
/>
4963
);
5064
};
5165

52-
const classNames = {
53-
tabLink: (css, theme) => css`
54-
text-decoration: none;
55-
color: ${theme.palette.text.secondary};
56-
font-size: 14px;
57-
display: block;
58-
padding: 0 16px 16px;
59-
60-
&:hover {
61-
color: ${theme.palette.text.primary};
62-
}
63-
`,
64-
activeTabLink: (css, theme) => css`
65-
color: ${theme.palette.text.primary};
66-
position: relative;
67-
68-
&:before {
69-
content: "";
70-
left: 0;
71-
bottom: 0;
72-
height: 2px;
73-
width: 100%;
74-
background: ${theme.palette.primary.main};
75-
position: absolute;
76-
}
77-
`,
78-
} satisfies Record<string, ClassName>;
66+
const styles = {
67+
tabLink: (theme) => ({
68+
textDecoration: "none",
69+
color: theme.palette.text.secondary,
70+
fontSize: 14,
71+
display: "block",
72+
padding: `${TAB_PADDING_Y}px 16px`,
73+
74+
"&:hover": {
75+
color: theme.palette.text.primary,
76+
},
77+
}),
78+
activeTabLink: (theme) => ({
79+
color: theme.palette.text.primary,
80+
position: "relative",
81+
82+
"&:before": {
83+
content: '""',
84+
left: 0,
85+
bottom: -1,
86+
height: 1,
87+
width: "100%",
88+
background: theme.palette.primary.main,
89+
position: "absolute",
90+
},
91+
}),
92+
} satisfies Record<string, Interpolation<Theme>>;

0 commit comments

Comments
 (0)