TabPane.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import React, { createRef, PureComponent, ReactNode } from 'react';
  2. import PropTypes from 'prop-types';
  3. import cls from 'classnames';
  4. import { cssClasses } from '@douyinfe/semi-foundation/tabs/constants';
  5. import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
  6. import TabsContext from './tabs-context';
  7. import { TabContextValue } from './interface';
  8. import TabPaneTransition from './TabPaneTransition';
  9. import { PlainTab, TabPaneProps } from './interface';
  10. class TabPane extends PureComponent<TabPaneProps> {
  11. static isTabPane = true;
  12. static contextType = TabsContext;
  13. static propTypes = {
  14. className: PropTypes.string,
  15. style: PropTypes.object,
  16. children: PropTypes.node,
  17. disabled: PropTypes.bool,
  18. itemKey: PropTypes.string,
  19. tab: PropTypes.node,
  20. icon: PropTypes.node,
  21. closable: PropTypes.bool
  22. };
  23. lastActiveKey: string = null;
  24. ref = createRef<HTMLDivElement>();
  25. isAnimating: boolean;
  26. _active: boolean;
  27. context: TabContextValue;
  28. componentDidMount(): void {
  29. this.lastActiveKey = this.context.activeKey;
  30. }
  31. // get direction from current item key to activeKey
  32. getDirection = (activeKey: string, itemKey: string, panes: Array<PlainTab>): boolean => {
  33. if (itemKey !== null && activeKey !== null && Array.isArray(panes) && panes.length) {
  34. const activeIndex = panes.findIndex(pane => pane.itemKey === activeKey);
  35. const itemIndex = panes.findIndex(pane => pane.itemKey === itemKey);
  36. const lastActiveIndex = panes.findIndex(pane => pane.itemKey === this.lastActiveKey);
  37. this.lastActiveKey = activeKey;
  38. if (activeIndex === itemIndex) {
  39. return lastActiveIndex > activeIndex;
  40. } else {
  41. return itemIndex < activeIndex;
  42. }
  43. }
  44. return false;
  45. };
  46. /* istanbul ignore next */
  47. hideScroll = (): void => {
  48. if (this.ref && this.ref.current) {
  49. this.ref.current.style.overflow = 'hidden';
  50. this.isAnimating = true;
  51. }
  52. };
  53. /* istanbul ignore next */
  54. autoScroll = (): void => {
  55. if (this.ref && this.ref.current) {
  56. this.ref.current.style.overflow = '';
  57. this.isAnimating = false;
  58. }
  59. };
  60. shouldRender = (): boolean => {
  61. const { itemKey } = this.props;
  62. const { activeKey, lazyRender } = this.context;
  63. const active = activeKey === itemKey;
  64. this._active = this._active || active;
  65. return lazyRender ? this._active : true;
  66. };
  67. render(): ReactNode {
  68. const { tabPaneMotion: motion, tabPosition } = this.context;
  69. const { className, style, children, itemKey, ...restProps } = this.props;
  70. const active = this.context.activeKey === itemKey;
  71. const classNames = cls(className, {
  72. [cssClasses.TABS_PANE_INACTIVE]: !active,
  73. [cssClasses.TABS_PANE_ACTIVE]: active,
  74. [cssClasses.TABS_PANE]: true,
  75. });
  76. const shouldRender = this.shouldRender();
  77. return (
  78. <div
  79. ref={this.ref}
  80. role="tabpanel"
  81. id={`semiTabPanel${itemKey}`}
  82. aria-labelledby={`semiTab${itemKey}`}
  83. className={classNames}
  84. style={style}
  85. aria-hidden={active ? 'false' : 'true'}
  86. tabIndex={0}
  87. {...getDataAttr(restProps)}
  88. x-semi-prop="children"
  89. >
  90. {motion ? (
  91. <TabPaneTransition
  92. direction={this.getDirection(this.context.activeKey, itemKey, this.context.panes)}
  93. motion={motion}
  94. mode={tabPosition === 'top' ? 'horizontal' : 'vertical'}
  95. state={active ? 'enter' : 'leave'}
  96. >
  97. {(transitionStyle): ReactNode => (
  98. <div
  99. className={`${cssClasses.TABS_PANE_MOTION_OVERLAY}`}
  100. style={{ ...transitionStyle }}
  101. x-semi-prop="children"
  102. >
  103. {shouldRender ? children : null}
  104. </div>
  105. )}
  106. </TabPaneTransition>
  107. ) : shouldRender ? (
  108. children
  109. ) : null}
  110. </div>
  111. );
  112. }
  113. }
  114. export default TabPane;