@@ -28,31 +28,28 @@ interface TabsProps {
2828 className ?: string ;
2929}
3030
31- export function Tabs ( {
31+ /** Inner component that uses useSearchParams — only rendered when syncWithUrl=true */
32+ function TabsWithUrl ( {
3233 defaultValue,
3334 value : controlledValue ,
3435 onValueChange,
35- syncWithUrl = false ,
3636 urlParam = "tab" ,
3737 children,
3838 className = "" ,
39- } : TabsProps ) {
39+ } : Omit < TabsProps , "syncWithUrl" > ) {
4040 const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
4141 const [ internalValue , setInternalValue ] = useState ( defaultValue || "" ) ;
4242
43- const urlValue = syncWithUrl ? searchParams . get ( urlParam ) : null ;
43+ const urlValue = searchParams . get ( urlParam ) ;
4444 const activeValue =
4545 controlledValue !== undefined
4646 ? controlledValue
47- : syncWithUrl && urlValue
47+ : urlValue
4848 ? urlValue
4949 : internalValue ;
5050
5151 useEffect ( ( ) => {
52- // If we're syncing with URL but the param isn't there, and we have a defaultValue,
53- // let's set the default value in the URL implicitly, or just let activeValue handle it.
54- // Setting it explicitly ensures deep links are predictable.
55- if ( syncWithUrl && ! urlValue && defaultValue ) {
52+ if ( ! urlValue && defaultValue ) {
5653 setSearchParams (
5754 ( prev ) => {
5855 const newParams = new URLSearchParams ( prev ) ;
@@ -62,24 +59,53 @@ export function Tabs({
6259 { replace : true }
6360 ) ;
6461 }
65- } , [ syncWithUrl , urlValue , defaultValue , urlParam , setSearchParams ] ) ;
62+ } , [ urlValue , defaultValue , urlParam , setSearchParams ] ) ;
6663
6764 const handleValueChange = ( newValue : string ) => {
6865 if ( controlledValue === undefined ) {
6966 setInternalValue ( newValue ) ;
7067 }
7168
72- if ( syncWithUrl ) {
73- setSearchParams (
74- ( prev ) => {
75- const newParams = new URLSearchParams ( prev ) ;
76- newParams . set ( urlParam , newValue ) ;
77- return newParams ;
78- } ,
79- { replace : true }
80- ) ;
69+ setSearchParams (
70+ ( prev ) => {
71+ const newParams = new URLSearchParams ( prev ) ;
72+ newParams . set ( urlParam , newValue ) ;
73+ return newParams ;
74+ } ,
75+ { replace : true }
76+ ) ;
77+
78+ if ( onValueChange ) {
79+ onValueChange ( newValue ) ;
8180 }
81+ } ;
82+
83+ return (
84+ < TabsContext . Provider value = { { value : activeValue , onValueChange : handleValueChange } } >
85+ < div className = { `tabs-root ${ className } ` } data-state = { activeValue } >
86+ { children }
87+ </ div >
88+ </ TabsContext . Provider >
89+ ) ;
90+ }
91+
92+ /** Inner component for tabs without URL sync */
93+ function TabsWithoutUrl ( {
94+ defaultValue,
95+ value : controlledValue ,
96+ onValueChange,
97+ children,
98+ className = "" ,
99+ } : Omit < TabsProps , "syncWithUrl" | "urlParam" > ) {
100+ const [ internalValue , setInternalValue ] = useState ( defaultValue || "" ) ;
101+
102+ const activeValue =
103+ controlledValue !== undefined ? controlledValue : internalValue ;
82104
105+ const handleValueChange = ( newValue : string ) => {
106+ if ( controlledValue === undefined ) {
107+ setInternalValue ( newValue ) ;
108+ }
83109 if ( onValueChange ) {
84110 onValueChange ( newValue ) ;
85111 }
@@ -94,6 +120,16 @@ export function Tabs({
94120 ) ;
95121}
96122
123+ export function Tabs ( {
124+ syncWithUrl = false ,
125+ ...props
126+ } : TabsProps ) {
127+ if ( syncWithUrl ) {
128+ return < TabsWithUrl { ...props } /> ;
129+ }
130+ return < TabsWithoutUrl { ...props } /> ;
131+ }
132+
97133export function TabsList ( { children, className = "" , style } : { children : ReactNode ; className ?: string ; style ?: React . CSSProperties } ) {
98134 return (
99135 < div
0 commit comments