Skip to content

Commit

Permalink
feat: TabContent, TabTrigger 에 ref 전달
Browse files Browse the repository at this point in the history
  • Loading branch information
SeieunYoo committed Sep 11, 2024
1 parent aa18d64 commit ec4ab33
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 52 deletions.
20 changes: 20 additions & 0 deletions packages/wow-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@
"require": "./dist/Tag.cjs",
"import": "./dist/Tag.js"
},
"./Tabs": {
"types": "./dist/components/Tabs/index.d.ts",
"require": "./dist/Tabs.cjs",
"import": "./dist/Tabs.js"
},
"./TabsContent": {
"types": "./dist/components/Tabs/TabsContent.d.ts",
"require": "./dist/TabsContent.cjs",
"import": "./dist/TabsContent.js"
},
"./TabsList": {
"types": "./dist/components/Tabs/TabsList.d.ts",
"require": "./dist/TabsList.cjs",
"import": "./dist/TabsList.js"
},
"./TabsTrigger": {
"types": "./dist/components/Tabs/TabsTrigger.d.ts",
"require": "./dist/TabsTrigger.cjs",
"import": "./dist/TabsTrigger.js"
},
"./Switch": {
"types": "./dist/components/Switch/index.d.ts",
"require": "./dist/Switch.cjs",
Expand Down
41 changes: 22 additions & 19 deletions packages/wow-ui/src/components/Tabs/TabsContent.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
"use client";

import type { PropsWithChildren } from "react";
import { forwardRef, type PropsWithChildren } from "react";

import { useTabContext } from "./contexts/TabContext";

interface TabsContentProps extends PropsWithChildren {
value: string;
}
export const TabsContent = ({
value: tabValue,
children,
}: TabsContentProps) => {
const { value, label } = useTabContext();
const selected = tabValue === value;
if (!selected) return;

return (
<div
aria-labelledby={`${label}-tab-trigger-${value}`}
id={`${label}-tab-content-${value}`}
role="tabpanel"
tabIndex={0}
>
{children}
</div>
);
};
export const TabsContent = forwardRef<HTMLDivElement, TabsContentProps>(
({ value: tabValue, children }: TabsContentProps, ref) => {
const { value, label } = useTabContext();
const selected = tabValue === value;
if (!selected) return null;

return (
<div
aria-labelledby={`${label}-tab-trigger-${value}`}
id={`${label}-tab-content-${value}`}
ref={ref}
role="tabpanel"
tabIndex={0}
>
{children}
</div>
);
}
);

TabsContent.displayName = "TabsContent";
73 changes: 40 additions & 33 deletions packages/wow-ui/src/components/Tabs/TabsTrigger.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"use client";

import { cva } from "@styled-system/css";
import { type PropsWithChildren, useEffect, useRef } from "react";
import { forwardRef, type PropsWithChildren, useEffect, useRef } from "react";

import { useMergeRefs } from "@/hooks/useMergeRefs";

import { useCollectionContext } from "./contexts/CollectionContext";
import { useTabContext } from "./contexts/TabContext";
Expand All @@ -10,42 +12,47 @@ interface TapTriggerProps extends PropsWithChildren {
value: string;
}

export const TabsTrigger = ({ value, children }: TapTriggerProps) => {
const { value: selectedValue, setSelectedValue, label } = useTabContext();
const selected = selectedValue === value;
export const TabsTrigger = forwardRef<HTMLButtonElement, TapTriggerProps>(
({ value, children }: TapTriggerProps, ref) => {
const { value: selectedValue, setSelectedValue, label } = useTabContext();
const selected = selectedValue === value;

const handleClickTabTrigger = () => {
setSelectedValue(value);
};

const handleClickTabTrigger = () => {
setSelectedValue(value);
};
const { values } = useCollectionContext();
const internalButtonRef = useRef<HTMLButtonElement>(null);
const buttonRef = useMergeRefs(ref, internalButtonRef);

const { values } = useCollectionContext();
const buttonRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
const currentItem = values?.find((item) => item === value);
if (!currentItem) {
values?.push(value);
}
if (selected && internalButtonRef.current) {
internalButtonRef.current.focus();
}
}, [values, selected, value]);

useEffect(() => {
const currentItem = values?.find((item) => item === value);
if (!currentItem) {
values?.push(value);
}
if (selected && buttonRef.current) {
buttonRef.current.focus();
}
}, [values, selected, value]);
return (
<button
aria-controls={`${label}-tab-content-${value}`}
aria-selected={selected}
className={tabTriggerStyle({ type: selected ? "selected" : "default" })}
id={`${label}-tab-trigger-${value}`}
ref={buttonRef}
role="tab"
tabIndex={selected ? 0 : -1}
onClick={handleClickTabTrigger}
>
{children}
</button>
);
}
);

return (
<button
aria-controls={`${label}-tab-content-${value}`}
aria-selected={selected}
className={tabTriggerStyle({ type: selected ? "selected" : "default" })}
id={`${label}-tab-trigger-${value}`}
ref={buttonRef}
role="tab"
tabIndex={selected ? 0 : -1}
onClick={handleClickTabTrigger}
>
{children}
</button>
);
};
TabsTrigger.displayName = "TabsTrigger";

const tabTriggerStyle = cva({
base: {
Expand Down
13 changes: 13 additions & 0 deletions packages/wow-ui/src/hooks/useMergeRefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { MutableRefObject, Ref } from "react";

export function useMergeRefs<T = any>(...refs: (Ref<T> | null)[]) {
return (value: T | null) => {
refs.forEach((ref) => {
if (typeof ref === "function") {
ref(value);
} else if (ref !== null && typeof ref === "object") {
(ref as MutableRefObject<T | null>).current = value;
}
});
};
}

0 comments on commit ec4ab33

Please sign in to comment.