Skip to content

Commit

Permalink
fix(tabs): simplify logic and add controlled usage
Browse files Browse the repository at this point in the history
  • Loading branch information
yyyyaaa committed Dec 1, 2023
1 parent d748295 commit cdabf69
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 17 deletions.
46 changes: 46 additions & 0 deletions packages/react/stories/Tabs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as React from "react";
import type { Meta, StoryObj } from "@storybook/react";

import Tabs from "../src/ui/tabs";
import Button from "../src/ui/button";
import Stack from "../src/ui/stack";

const meta: Meta<typeof Tabs> = {
component: Tabs,
Expand Down Expand Up @@ -32,3 +34,47 @@ export const Primary: Story = {
],
},
};

export const ControlledTabs: Story = {
args: {
tabs: [
{
label: "Tab One",
content: <h1>Tab1</h1>,
},
{
label: "Tab Two",
content: <h1>Tab2</h1>,
},
{
label: "Tab Three",
content: <h1>Tab3</h1>,
},
],
},
render: (props) => {
const [activeTab, setActiveTab] = React.useState(0);

return (
<>
<Tabs
tabs={props.tabs}
activeTab={activeTab}
onActiveTabChange={(tabId) => setActiveTab(tabId)}
/>
Click to change tab
<Stack direction="horizontal" space="$4">
<Button size="sm" onClick={() => setActiveTab(0)}>
tab 1
</Button>
<Button size="sm" onClick={() => setActiveTab(1)}>
tab 2
</Button>
<Button size="sm" onClick={() => setActiveTab(2)}>
tab 3
</Button>
</Stack>
</>
);
},
};
49 changes: 32 additions & 17 deletions src/ui/tabs/tabs.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
onMount,
onUnMount,
} from "@builder.io/mitosis";
import { assignInlineVars } from "@vanilla-extract/dynamic";
import clsx from "clsx";
import Box from "../box";
import Text from "../text";
Expand Down Expand Up @@ -40,23 +39,35 @@ export default function Tabs(props: TabsProps) {
width: number;
transform: string;
setActiveStyles: (activeTab: number) => void;
isControlled: () => boolean;
getActiveTabId: () => number;
}>({
isMounted: false,
theme: "light",
active: 0,
width: 0,
transform: "translateX(0)",
isControlled() {
return typeof props.activeTab !== "undefined";
},
getActiveTabId() {
return state.isControlled() ? props.activeTab : state.active;
},
findActiveTabContent() {
const finalActiveTab = state.getActiveTabId();

const panel: TabProps | null = props?.tabs
? props?.tabs.find((_, index) => index === state.active) ?? null
? props?.tabs.find((_, index) => index === finalActiveTab) ?? null
: null;
return panel?.content ?? null;
},
getBgColor() {
return state.theme === "light" ? "$gray200" : "$gray800";
},
getTextColor(tabIndex: number) {
if (tabIndex !== state.active) {
const finalActiveTab = state.getActiveTabId();

if (tabIndex !== finalActiveTab) {
return "$textSecondary";
}
return state.theme === "light" ? "$white" : "$gray900";
Expand All @@ -73,18 +84,19 @@ export default function Tabs(props: TabsProps) {
},
});
let cleanupRef = useRef<() => void>(null);
let activeTabContentRef = useRef(null);

onMount(() => {
state.theme = store.getState().theme;
state.isMounted = true;

setTimeout(() => {
state.setActiveStyles(props.defaultActiveTab ?? state.active);
const finalActiveTab = state.getActiveTabId();
state.setActiveStyles(props.defaultActiveTab ?? finalActiveTab);
}, 100);

const handleResize = () => {
state.setActiveStyles(state.active);
const finalActiveTab = state.getActiveTabId();
state.setActiveStyles(finalActiveTab);
};

window.addEventListener("resize", handleResize, true);
Expand All @@ -95,16 +107,18 @@ export default function Tabs(props: TabsProps) {
});
});

onUpdate(() => {
// Only apply this effect for controlled component
if (!state.isControlled()) return;
state.setActiveStyles(props.activeTab);
}, [props.activeTab]);

onUnMount(() => {
if (typeof cleanupRef === "function") {
cleanupRef();
}
});

onUpdate(() => {
activeTabContentRef = state.findActiveTabContent();
}, [state.active]);

return (
<Box className={props.className} {...props.attributes}>
<Box
Expand Down Expand Up @@ -151,7 +165,7 @@ export default function Tabs(props: TabsProps) {
attributes={{
role: "tab",
"data-tab-key": `tab-${index}`,
"aria-selected": state.active === index,
"aria-selected": state.getActiveTabId() === index,
"aria-controls": `tabpanel-${index}`,
}}
key={tab.label}
Expand All @@ -161,7 +175,9 @@ export default function Tabs(props: TabsProps) {
className={styles.tabButton}
attributes={{
onClick: () => {
state.active = index;
if (!state.isControlled()) {
state.active = index;
}
state.setActiveStyles(index);
},
}}
Expand All @@ -187,7 +203,7 @@ export default function Tabs(props: TabsProps) {
desktop: "$7",
},
borderRadius: "50px",
zIndex: state.active === index ? -1 : undefined,
zIndex: state.getActiveTabId() === index ? -1 : undefined,
}}
>
{tab.label}
Expand All @@ -202,12 +218,11 @@ export default function Tabs(props: TabsProps) {
<Box
attributes={{
role: "tabpanel",
"aria-labelledby": `btn-${state.active}`,
"data-tab-panel-key": `tabpanel-${state.active}`,
"aria-labelledby": `btn-${state.getActiveTabId()}`,
"data-tab-panel-key": `tabpanel-${state.getActiveTabId()}`,
}}
>
<Show when={state.isMounted}>{activeTabContentRef}</Show>
<Show when={!state.isMounted}>{state.findActiveTabContent()}</Show>
{state.findActiveTabContent()}
</Box>
</Box>
</Box>
Expand Down
2 changes: 2 additions & 0 deletions src/ui/tabs/tabs.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export type TabProps = {

export interface TabsProps extends BaseComponentProps {
defaultActiveTab?: number;
activeTab?: number;
onActiveTabChange?: (tabId: number) => void;
tabs: TabProps[];
attributes?: BoxProps;
}

0 comments on commit cdabf69

Please sign in to comment.