Skip to content

Commit b916b32

Browse files
Update tabs API (#1700)
1 parent 50c4fd3 commit b916b32

File tree

11 files changed

+741
-533
lines changed

11 files changed

+741
-533
lines changed

packages/@react-aria/tabs/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
export * from './useTabs';
13+
export * from './useTab';
14+
export * from './useTabPanel';
15+
export * from './useTabList';
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2020 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {AriaTabProps} from '@react-types/tabs';
14+
import {generateId} from './utils';
15+
import {HTMLAttributes, RefObject} from 'react';
16+
import {SingleSelectListState} from '@react-stately/list';
17+
import {usePress} from '@react-aria/interactions';
18+
import {useSelectableItem} from '@react-aria/selection';
19+
20+
interface TabAria {
21+
/** Props for the tab element. */
22+
tabProps: HTMLAttributes<HTMLElement>
23+
}
24+
25+
export function useTab<T>(
26+
props: AriaTabProps,
27+
state: SingleSelectListState<T>,
28+
ref: RefObject<HTMLElement>
29+
): TabAria {
30+
let {key, isDisabled: propsDisabled} = props;
31+
let {selectionManager: manager, selectedKey} = state;
32+
33+
let isSelected = key === selectedKey;
34+
35+
let {itemProps} = useSelectableItem({
36+
selectionManager: manager,
37+
key,
38+
ref
39+
});
40+
let isDisabled = propsDisabled || state.disabledKeys.has(key);
41+
42+
let {pressProps} = usePress({...itemProps, isDisabled});
43+
let tabId = generateId(state, key, 'tab');
44+
let tabPanelId = generateId(state, key, 'tabpanel');
45+
let {tabIndex} = pressProps;
46+
47+
return {
48+
tabProps: {
49+
...pressProps,
50+
id: tabId,
51+
'aria-selected': isSelected,
52+
'aria-disabled': isDisabled || undefined,
53+
'aria-controls': isSelected ? tabPanelId : undefined,
54+
tabIndex: isDisabled ? undefined : tabIndex,
55+
role: 'tab'
56+
}
57+
};
58+
}
59+
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2020 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {AriaTabListProps} from '@react-types/tabs';
14+
import {HTMLAttributes, useMemo} from 'react';
15+
import {mergeProps, useId, useLabels} from '@react-aria/utils';
16+
import {TabListState} from '@react-stately/tabs';
17+
import {tabsIds} from './utils';
18+
import {TabsKeyboardDelegate} from './TabsKeyboardDelegate';
19+
import {useLocale} from '@react-aria/i18n';
20+
import {useSelectableCollection} from '@react-aria/selection';
21+
22+
interface TabListAria {
23+
/** Props for the tablist container. */
24+
tabListProps: HTMLAttributes<HTMLElement>
25+
}
26+
27+
export function useTabList<T>(props: AriaTabListProps<T>, state: TabListState<T>, ref): TabListAria {
28+
let {
29+
orientation = 'horizontal',
30+
keyboardActivation = 'automatic'
31+
} = props;
32+
let {
33+
collection,
34+
selectionManager: manager,
35+
disabledKeys
36+
} = state;
37+
let {direction} = useLocale();
38+
let delegate = useMemo(() => new TabsKeyboardDelegate(
39+
collection,
40+
direction,
41+
orientation,
42+
disabledKeys), [collection, disabledKeys, orientation, direction]);
43+
44+
let {collectionProps} = useSelectableCollection({
45+
ref,
46+
selectionManager: manager,
47+
keyboardDelegate: delegate,
48+
selectOnFocus: keyboardActivation === 'automatic',
49+
disallowEmptySelection: true
50+
});
51+
52+
// Compute base id for all tabs
53+
let tabsId = useId();
54+
tabsIds.set(state, tabsId);
55+
56+
let tabListLabelProps = useLabels({...props, id: tabsId});
57+
58+
return {
59+
tabListProps: {
60+
...mergeProps(collectionProps, tabListLabelProps),
61+
role: 'tablist',
62+
'aria-orientation': orientation,
63+
tabIndex: undefined
64+
}
65+
};
66+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2020 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {AriaTabPanelProps} from '@react-types/tabs';
14+
import {generateId} from './utils';
15+
import {HTMLAttributes} from 'react';
16+
import {mergeProps} from '@react-aria/utils';
17+
import {TabListState} from '@react-stately/tabs';
18+
19+
interface TabPanelAria {
20+
/** Props for the tab panel element. */
21+
tabPanelProps: HTMLAttributes<HTMLElement>
22+
}
23+
24+
export function useTabPanel<T>(props: AriaTabPanelProps, state: TabListState<T>): TabPanelAria {
25+
return {
26+
tabPanelProps: mergeProps(props, {
27+
id: generateId(state, state?.selectedKey, 'tabpanel'),
28+
'aria-labelledby': generateId(state, state?.selectedKey, 'tab'),
29+
tabIndex: 0,
30+
role: 'tabpanel'
31+
})
32+
};
33+
}

packages/@react-aria/tabs/src/useTabs.ts

Lines changed: 0 additions & 133 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2020 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {Key} from 'react';
14+
import {SingleSelectListState} from '@react-stately/list';
15+
16+
export const tabsIds = new WeakMap<SingleSelectListState<unknown>, string>();
17+
18+
export function generateId<T>(state: SingleSelectListState<T>, key: Key, role: string) {
19+
if (typeof key === 'string') {
20+
key = key.replace(/\s+/g, '');
21+
}
22+
23+
let baseId = tabsIds.get(state);
24+
return `${baseId}-${role}-${key}`;
25+
}
26+

0 commit comments

Comments
 (0)