tabPanels.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import {useContext, useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import {AriaTabPanelProps, useTabPanel} from '@react-aria/tabs';
  4. import {useCollection} from '@react-stately/collections';
  5. import {ListCollection} from '@react-stately/list';
  6. import {TabListState} from '@react-stately/tabs';
  7. import {CollectionBase, Node, Orientation} from '@react-types/shared';
  8. import {TabsContext} from './index';
  9. import {Item} from './item';
  10. import {tabsShouldForwardProp} from './utils';
  11. const collectionFactory = (nodes: Iterable<Node<any>>) => new ListCollection(nodes);
  12. interface TabPanelsProps extends AriaTabPanelProps, CollectionBase<any> {
  13. className?: string;
  14. }
  15. /**
  16. * To be used as a direct child of the <Tabs /> component. See example usage
  17. * in tabs.stories.js
  18. */
  19. export function TabPanels(props: TabPanelsProps) {
  20. const {
  21. rootProps: {orientation, items},
  22. tabListState,
  23. } = useContext(TabsContext);
  24. // Parse child tab panels from props and identify the selected panel
  25. const collection = useCollection({items, ...props}, collectionFactory, {
  26. suppressTextValueWarning: true,
  27. });
  28. const selectedPanel = tabListState
  29. ? collection.getItem(tabListState.selectedKey)
  30. : null;
  31. if (!tabListState) {
  32. return null;
  33. }
  34. return (
  35. <TabPanel
  36. {...props}
  37. state={tabListState}
  38. orientation={orientation}
  39. key={tabListState?.selectedKey}
  40. >
  41. {selectedPanel?.props.children}
  42. </TabPanel>
  43. );
  44. }
  45. TabPanels.Item = Item;
  46. interface TabPanelProps extends AriaTabPanelProps {
  47. state: TabListState<any>;
  48. children?: React.ReactNode;
  49. className?: string;
  50. orientation?: Orientation;
  51. }
  52. function TabPanel({
  53. state,
  54. orientation = 'horizontal',
  55. className,
  56. children,
  57. ...props
  58. }: TabPanelProps) {
  59. const ref = useRef<HTMLDivElement>(null);
  60. const {tabPanelProps} = useTabPanel(props, state, ref);
  61. return (
  62. <TabPanelWrap
  63. {...tabPanelProps}
  64. orientation={orientation}
  65. className={className}
  66. ref={ref}
  67. >
  68. {children}
  69. </TabPanelWrap>
  70. );
  71. }
  72. const TabPanelWrap = styled('div', {shouldForwardProp: tabsShouldForwardProp})<{
  73. orientation: Orientation;
  74. }>`
  75. border-radius: ${p => p.theme.borderRadius};
  76. ${p => (p.orientation === 'horizontal' ? `height: 100%;` : `width: 100%;`)};
  77. &.focus-visible {
  78. outline: none;
  79. box-shadow:
  80. inset ${p => p.theme.focusBorder} 0 0 0 1px,
  81. ${p => p.theme.focusBorder} 0 0 0 1px;
  82. z-index: 1;
  83. }
  84. `;