12
12
13
13
import { classNames , SlotProvider , unwrapDOMRef , useDOMRef , useStyleProps } from '@react-spectrum/utils' ;
14
14
import { DOMProps , DOMRef , Node , Orientation } from '@react-types/shared' ;
15
- import { filterDOMProps , useValueEffect } from '@react-aria/utils' ;
15
+ import { filterDOMProps } from '@react-aria/utils' ;
16
16
import { FocusRing } from '@react-aria/focus' ;
17
17
import { Item , Picker } from '@react-spectrum/picker' ;
18
18
import { ListCollection , SingleSelectListState } from '@react-stately/list' ;
@@ -36,7 +36,7 @@ interface TabsContext<T> {
36
36
tabListState : TabListState < T > ,
37
37
setTabListState : ( state : TabListState < T > ) => void ,
38
38
selectedTab : HTMLElement ,
39
- collapse : boolean
39
+ collapsed : boolean
40
40
} ,
41
41
refs : {
42
42
wrapperRef : MutableRefObject < HTMLDivElement > ,
@@ -64,7 +64,7 @@ function Tabs<T extends object>(props: SpectrumTabsProps<T>, ref: DOMRef<HTMLDiv
64
64
65
65
let { direction} = useLocale ( ) ;
66
66
let { styleProps} = useStyleProps ( otherProps ) ;
67
- let [ collapse , setCollapse ] = useValueEffect ( false ) ;
67
+ let [ collapsed , setCollapsed ] = useState ( false ) ;
68
68
let [ selectedTab , setSelectedTab ] = useState < HTMLElement > ( ) ;
69
69
const [ tabListState , setTabListState ] = useState < TabListState < T > > ( null ) ;
70
70
@@ -77,34 +77,21 @@ function Tabs<T extends object>(props: SpectrumTabsProps<T>, ref: DOMRef<HTMLDiv
77
77
}
78
78
}
79
79
// collapse is in the dep array so selectedTab can be updated for TabLine positioning
80
- } , [ children , tabListState ?. selectedKey , collapse , tablistRef ] ) ;
80
+ } , [ children , tabListState ?. selectedKey , collapsed , tablistRef ] ) ;
81
81
82
82
let checkShouldCollapse = useCallback ( ( ) => {
83
- let computeShouldCollapse = ( ) => {
84
- if ( wrapperRef . current ) {
85
- let tabsComponent = wrapperRef . current ;
86
- let tabs = tablistRef . current . querySelectorAll ( '[role="tab"]' ) ;
87
- let lastTab = tabs [ tabs . length - 1 ] ;
88
-
89
- let end = direction === 'rtl' ? 'left' : 'right' ;
90
- let farEdgeTabList = tabsComponent . getBoundingClientRect ( ) [ end ] ;
91
- let farEdgeLastTab = lastTab ?. getBoundingClientRect ( ) [ end ] ;
92
- let shouldCollapse = direction === 'rtl' ? farEdgeLastTab < farEdgeTabList : farEdgeTabList < farEdgeLastTab ;
93
-
94
- return shouldCollapse ;
95
- }
96
- } ;
97
-
98
- if ( orientation !== 'vertical' ) {
99
- setCollapse ( function * ( ) {
100
- // Make Tabs render in non-collapsed state
101
- yield false ;
102
-
103
- // Compute if Tabs should collapse and update
104
- yield computeShouldCollapse ( ) ;
105
- } ) ;
83
+ if ( wrapperRef . current && orientation !== 'vertical' ) {
84
+ let tabsComponent = wrapperRef . current ;
85
+ let tabs = tablistRef . current . querySelectorAll ( '[role="tab"]' ) ;
86
+ let lastTab = tabs [ tabs . length - 1 ] ;
87
+
88
+ let end = direction === 'rtl' ? 'left' : 'right' ;
89
+ let farEdgeTabList = tabsComponent . getBoundingClientRect ( ) [ end ] ;
90
+ let farEdgeLastTab = lastTab ?. getBoundingClientRect ( ) [ end ] ;
91
+ let shouldCollapse = direction === 'rtl' ? farEdgeLastTab < farEdgeTabList : farEdgeTabList < farEdgeLastTab ;
92
+ setCollapsed ( shouldCollapse ) ;
106
93
}
107
- } , [ tablistRef , wrapperRef , direction , orientation , setCollapse ] ) ;
94
+ } , [ tablistRef , wrapperRef , direction , orientation , setCollapsed ] ) ;
108
95
109
96
useEffect ( ( ) => {
110
97
checkShouldCollapse ( ) ;
@@ -118,14 +105,14 @@ function Tabs<T extends object>(props: SpectrumTabsProps<T>, ref: DOMRef<HTMLDiv
118
105
119
106
// When the tabs are collapsed, the tabPanel should be labelled by the Picker button element.
120
107
let collapsibleTabListId = useId ( ) ;
121
- if ( collapse && orientation !== 'vertical' ) {
108
+ if ( collapsed && orientation !== 'vertical' ) {
122
109
tabPanelProps [ 'aria-labelledby' ] = collapsibleTabListId ;
123
110
}
124
111
return (
125
112
< TabContext . Provider
126
113
value = { {
127
114
tabProps : { ...props , orientation, density} ,
128
- tabState : { tabListState, setTabListState, selectedTab, collapse } ,
115
+ tabState : { tabListState, setTabListState, selectedTab, collapsed } ,
129
116
refs : { tablistRef, wrapperRef} ,
130
117
tabPanelProps
131
118
} } >
@@ -255,7 +242,7 @@ export function TabList<T>(props: SpectrumTabListProps<T>) {
255
242
const tabContext = useContext ( TabContext ) ;
256
243
const { refs, tabState, tabProps, tabPanelProps} = tabContext ;
257
244
const { isQuiet, density, isDisabled, isEmphasized, orientation} = tabProps ;
258
- const { selectedTab, collapse , setTabListState} = tabState ;
245
+ const { selectedTab, collapsed , setTabListState} = tabState ;
259
246
const { tablistRef, wrapperRef} = refs ;
260
247
// Pass original Tab props but override children to create the collection.
261
248
const state = useTabListState ( { ...tabProps , children : props . children } ) ;
@@ -268,12 +255,18 @@ export function TabList<T>(props: SpectrumTabListProps<T>) {
268
255
setTabListState ( state ) ;
269
256
// eslint-disable-next-line react-hooks/exhaustive-deps
270
257
} , [ state . disabledKeys , state . selectedItem , state . selectedKey , props . children ] ) ;
271
- let stylePropsForVertical = orientation === 'vertical' ? styleProps : { } ;
258
+
259
+ let collapseStyle : React . CSSProperties = collapsed && orientation !== 'vertical' ? { maxWidth : 'calc(100% + 1px)' , overflow : 'hidden' , visibility : 'hidden' , position : 'absolute' } : { maxWidth : 'calc(100% + 1px)' } ;
260
+ let stylePropsFinal = orientation === 'vertical' ? styleProps : { style : collapseStyle } ;
261
+
262
+ if ( collapsed && orientation !== 'vertical' ) {
263
+ tabListProps [ 'aria-hidden' ] = true ;
264
+ }
272
265
273
266
let tabListclassName = classNames ( styles , 'spectrum-TabsPanel-tabs' ) ;
274
267
const tabContent = (
275
268
< div
276
- { ...stylePropsForVertical }
269
+ { ...stylePropsFinal }
277
270
{ ...tabListProps }
278
271
ref = { tablistRef }
279
272
className = { classNames (
@@ -309,7 +302,8 @@ export function TabList<T>(props: SpectrumTabListProps<T>) {
309
302
'spectrum-TabsPanel-collapseWrapper' ,
310
303
styleProps . className
311
304
) } >
312
- { collapse ? < TabPicker { ...props } { ...tabProps } id = { tabPanelProps [ 'aria-labelledby' ] } state = { state } className = { tabListclassName } /> : tabContent }
305
+ < TabPicker { ...props } { ...tabProps } visible = { collapsed } id = { tabPanelProps [ 'aria-labelledby' ] } state = { state } className = { tabListclassName } />
306
+ { tabContent }
313
307
</ div >
314
308
) ;
315
309
}
@@ -359,7 +353,8 @@ interface TabPickerProps<T> extends Omit<SpectrumPickerProps<T>, 'children'> {
359
353
density ?: 'compact' | 'regular' ,
360
354
isEmphasized ?: boolean ,
361
355
state : SingleSelectListState < T > ,
362
- className ?: string
356
+ className ?: string ,
357
+ visible : boolean
363
358
}
364
359
365
360
function TabPicker < T > ( props : TabPickerProps < T > ) {
@@ -372,7 +367,8 @@ function TabPicker<T>(props: TabPickerProps<T>) {
372
367
'aria-label' : ariaLabel ,
373
368
density,
374
369
className,
375
- id
370
+ id,
371
+ visible
376
372
} = props ;
377
373
378
374
let ref = useRef ( ) ;
@@ -394,6 +390,8 @@ function TabPicker<T>(props: TabPickerProps<T>) {
394
390
'aria-label' : ariaLabel
395
391
} ;
396
392
393
+ const style : React . CSSProperties = visible ? { } : { visibility : 'hidden' , position : 'absolute' } ;
394
+
397
395
// TODO: Figure out if tabListProps should go onto the div here, v2 doesn't do it
398
396
return (
399
397
< div
@@ -408,7 +406,9 @@ function TabPicker<T>(props: TabPickerProps<T>) {
408
406
'spectrum-Tabs--emphasized' : isEmphasized
409
407
} ,
410
408
className
411
- ) } >
409
+ ) }
410
+ style = { style }
411
+ aria-hidden = { visible ? undefined : true } >
412
412
< SlotProvider
413
413
slots = { {
414
414
icon : {
@@ -425,7 +425,7 @@ function TabPicker<T>(props: TabPickerProps<T>) {
425
425
items = { items }
426
426
ref = { ref }
427
427
isQuiet
428
- isDisabled = { isDisabled }
428
+ isDisabled = { ! visible || isDisabled }
429
429
selectedKey = { state . selectedKey }
430
430
disabledKeys = { state . disabledKeys }
431
431
onSelectionChange = { state . setSelectedKey } >
0 commit comments