TabPane.tsx 4.1 KB

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