TabPane.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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 { PlainTab, TabContextValue, TabPaneProps } from './interface';
  8. import CSSAnimation from "../_cssAnimation";
  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. ref = createRef<HTMLDivElement>();
  23. _active: boolean;
  24. context: TabContextValue;
  25. // get direction from current item key to activeKey
  26. getDirection = (activeKey: string, itemKey: string, panes: Array<PlainTab>, lastActiveKey: string): boolean => {
  27. if (itemKey !== null && activeKey !== null && Array.isArray(panes) && panes.length) {
  28. const activeIndex = panes.findIndex(pane => pane.itemKey === activeKey);
  29. const itemIndex = panes.findIndex(pane => pane.itemKey === itemKey);
  30. const lastActiveIndex = panes.findIndex(pane => pane.itemKey === lastActiveKey);
  31. if (activeIndex === itemIndex) {
  32. return lastActiveIndex > activeIndex;
  33. } else {
  34. return itemIndex < activeIndex;
  35. }
  36. }
  37. return false;
  38. };
  39. shouldRender = (): boolean => {
  40. const { itemKey } = this.props;
  41. const { activeKey, lazyRender } = this.context;
  42. const active = activeKey === itemKey;
  43. this._active = this._active || active;
  44. return lazyRender ? this._active : true;
  45. };
  46. render(): ReactNode {
  47. const { tabPaneMotion: motion, tabPosition, prevActiveKey } = this.context;
  48. const { className, style, children, itemKey, ...restProps } = this.props;
  49. const active = this.context.activeKey === itemKey;
  50. const classNames = cls(className, {
  51. [cssClasses.TABS_PANE_INACTIVE]: !active,
  52. [cssClasses.TABS_PANE_ACTIVE]: active,
  53. [cssClasses.TABS_PANE]: true,
  54. });
  55. const shouldRender = this.shouldRender();
  56. const startClassName = (() => {
  57. const direction = this.getDirection(this.context.activeKey, itemKey, this.context.panes, prevActiveKey);
  58. if (tabPosition === 'top') {
  59. if (direction) {
  60. return cssClasses.TABS_PANE_ANIMATE_RIGHT_SHOW;
  61. } else {
  62. return cssClasses.TABS_PANE_ANIMATE_LEFT_SHOW;
  63. }
  64. } else {
  65. if (direction) {
  66. return cssClasses.TABS_PANE_ANIMATE_BOTTOM_SHOW;
  67. } else {
  68. return cssClasses.TABS_PANE_ANIMATE_TOP_SHOW;
  69. }
  70. }
  71. })();
  72. const isActivatedBecauseOtherTabPaneRemoved = !this.context.panes.find(tabPane => tabPane.itemKey === prevActiveKey);
  73. const hasMotion = motion && active && !isActivatedBecauseOtherTabPaneRemoved && !this.context.forceDisableMotion;
  74. return (
  75. <div
  76. ref={this.ref}
  77. role="tabpanel"
  78. id={`semiTabPanel${itemKey}`}
  79. aria-labelledby={`semiTab${itemKey}`}
  80. className={classNames}
  81. style={style}
  82. aria-hidden={active ? 'false' : 'true'}
  83. tabIndex={0}
  84. {...getDataAttr(restProps)}
  85. x-semi-prop="children"
  86. >
  87. <CSSAnimation motion={hasMotion} animationState={active ? "enter" : "leave"}
  88. startClassName={startClassName}>
  89. {
  90. ({ animationClassName, animationEventsNeedBind }) => {
  91. return <div
  92. className={`${cssClasses.TABS_PANE_MOTION_OVERLAY} ${animationClassName}`}
  93. x-semi-prop="children"
  94. {...animationEventsNeedBind}
  95. >
  96. {shouldRender ? children : null}
  97. </div>;
  98. }
  99. }
  100. </CSSAnimation>
  101. </div>
  102. );
  103. }
  104. }
  105. export default TabPane;