Skip to content
4 changes: 3 additions & 1 deletion packages/@react-aria/tabs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@
* governing permissions and limitations under the License.
*/

export * from './useTabs';
export * from './useTab';
export * from './useTabPanel';
export * from './useTabList';
59 changes: 59 additions & 0 deletions packages/@react-aria/tabs/src/useTab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {AriaTabProps} from '@react-types/tabs';
import {generateId} from './utils';
import {HTMLAttributes, RefObject} from 'react';
import {SingleSelectListState} from '@react-stately/list';
import {usePress} from '@react-aria/interactions';
import {useSelectableItem} from '@react-aria/selection';

interface TabAria {
/** Props for the tab element. */
tabProps: HTMLAttributes<HTMLElement>
}

export function useTab<T>(
props: AriaTabProps,
state: SingleSelectListState<T>,
ref: RefObject<HTMLElement>
): TabAria {
let {key, isDisabled: propsDisabled} = props;
let {selectionManager: manager, selectedKey} = state;

let isSelected = key === selectedKey;

let {itemProps} = useSelectableItem({
selectionManager: manager,
key,
ref
});
let isDisabled = propsDisabled || state.disabledKeys.has(key);

let {pressProps} = usePress({...itemProps, isDisabled});
let tabId = generateId(state, key, 'tab');
let tabPanelId = generateId(state, key, 'tabpanel');
let {tabIndex} = pressProps;

return {
tabProps: {
...pressProps,
id: tabId,
'aria-selected': isSelected,
'aria-disabled': isDisabled || undefined,
'aria-controls': isSelected ? tabPanelId : undefined,
tabIndex: isDisabled ? undefined : tabIndex,
role: 'tab'
}
};
}

66 changes: 66 additions & 0 deletions packages/@react-aria/tabs/src/useTabList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {AriaTabListProps} from '@react-types/tabs';
import {HTMLAttributes, useMemo} from 'react';
import {mergeProps, useId, useLabels} from '@react-aria/utils';
import {TabListState} from '@react-stately/tabs';
import {tabsIds} from './utils';
import {TabsKeyboardDelegate} from './TabsKeyboardDelegate';
import {useLocale} from '@react-aria/i18n';
import {useSelectableCollection} from '@react-aria/selection';

interface TabListAria {
/** Props for the tablist container. */
tabListProps: HTMLAttributes<HTMLElement>
}

export function useTabList<T>(props: AriaTabListProps<T>, state: TabListState<T>, ref): TabListAria {
let {
orientation = 'horizontal',
keyboardActivation = 'automatic'
} = props;
let {
collection,
selectionManager: manager,
disabledKeys
} = state;
let {direction} = useLocale();
let delegate = useMemo(() => new TabsKeyboardDelegate(
collection,
direction,
orientation,
disabledKeys), [collection, disabledKeys, orientation, direction]);

let {collectionProps} = useSelectableCollection({
ref,
selectionManager: manager,
keyboardDelegate: delegate,
selectOnFocus: keyboardActivation === 'automatic',
disallowEmptySelection: true
});

// Compute base id for all tabs
let tabsId = useId();
tabsIds.set(state, tabsId);

let tabListLabelProps = useLabels({...props, id: tabsId});

return {
tabListProps: {
...mergeProps(collectionProps, tabListLabelProps),
role: 'tablist',
'aria-orientation': orientation,
tabIndex: undefined
}
};
}
33 changes: 33 additions & 0 deletions packages/@react-aria/tabs/src/useTabPanel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {AriaTabPanelProps} from '@react-types/tabs';
import {generateId} from './utils';
import {HTMLAttributes} from 'react';
import {mergeProps} from '@react-aria/utils';
import {TabListState} from '@react-stately/tabs';

interface TabPanelAria {
/** Props for the tab panel element. */
tabPanelProps: HTMLAttributes<HTMLElement>
}

export function useTabPanel<T>(props: AriaTabPanelProps, state: TabListState<T>): TabPanelAria {
return {
tabPanelProps: mergeProps(props, {
id: generateId(state, state?.selectedKey, 'tabpanel'),
'aria-labelledby': generateId(state, state?.selectedKey, 'tab'),
tabIndex: 0,
role: 'tabpanel'
})
};
}
133 changes: 0 additions & 133 deletions packages/@react-aria/tabs/src/useTabs.ts

This file was deleted.

26 changes: 26 additions & 0 deletions packages/@react-aria/tabs/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {Key} from 'react';
import {SingleSelectListState} from '@react-stately/list';

export const tabsIds = new WeakMap<SingleSelectListState<unknown>, string>();

export function generateId<T>(state: SingleSelectListState<T>, key: Key, role: string) {
if (typeof key === 'string') {
key = key.replace(/\s+/g, '');
}

let baseId = tabsIds.get(state);
return `${baseId}-${role}-${key}`;
}

Loading