CustomExpandIcon.tsx 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /* eslint-disable react-hooks/exhaustive-deps */
  2. import React, { useCallback } from 'react';
  3. import PropTypes from 'prop-types';
  4. import { noop } from 'lodash';
  5. import { IconChevronRight, IconChevronDown, IconTreeTriangleDown, IconTreeTriangleRight } from '@douyinfe/semi-icons';
  6. import { cssClasses } from '@douyinfe/semi-foundation/table/constants';
  7. import isEnterPress from '@douyinfe/semi-foundation/utils/isEnterPress';
  8. import CSSAnimation from "../_cssAnimation";
  9. export interface CustomExpandIconProps {
  10. expanded?: boolean;
  11. componentType?: 'tree' | 'expand';
  12. onClick?: (nextExpand: boolean, e: React.MouseEvent<HTMLDivElement>) => void;
  13. onMouseEnter?: (e: React.MouseEvent<HTMLSpanElement>) => void;
  14. onMouseLeave?: (e: React.MouseEvent<HTMLSpanElement>) => void;
  15. expandIcon?: ((expanded?: boolean) => React.ReactNode) | React.ReactNode;
  16. prefixCls?: string;
  17. motion?: boolean
  18. }
  19. /**
  20. * render expand icon
  21. */
  22. export default function CustomExpandIcon(props: CustomExpandIconProps) {
  23. const {
  24. expanded,
  25. componentType = 'expand',
  26. onClick = noop,
  27. onMouseEnter = noop,
  28. onMouseLeave = noop,
  29. expandIcon,
  30. prefixCls = cssClasses.PREFIX,
  31. motion = true,
  32. } = props;
  33. let icon;
  34. if (React.isValidElement(expandIcon)) {
  35. icon = expandIcon;
  36. } else if (typeof expandIcon === 'function') {
  37. icon = expandIcon(expanded);
  38. } else if (componentType === 'tree') {
  39. icon = expanded && !motion ? <IconTreeTriangleDown size="small" /> : <IconTreeTriangleRight size="small" />;
  40. } else {
  41. icon = expanded && !motion ? <IconChevronDown /> : <IconChevronRight />;
  42. }
  43. const handleClick = useCallback(
  44. e => {
  45. if (typeof onClick === 'function') {
  46. onClick(!expanded, e);
  47. }
  48. },
  49. [expanded]
  50. );
  51. if (motion) {
  52. const originIcon = icon;
  53. icon = <CSSAnimation animationState={expanded ? "enter" : "leave"} startClassName={`${cssClasses.PREFIX}-expandedIcon-${expanded ? 'show' : "hide"}`}>
  54. {({ animationClassName }) => {
  55. return React.cloneElement(originIcon, { className: (originIcon.props.className || "") + " " + animationClassName });
  56. }}
  57. </CSSAnimation>;
  58. }
  59. return (
  60. <span
  61. role="button"
  62. aria-label="Expand this row"
  63. tabIndex={-1}
  64. onClick={handleClick}
  65. onMouseEnter={onMouseEnter}
  66. onMouseLeave={onMouseLeave}
  67. className={`${prefixCls}-expand-icon`}
  68. onKeyPress={e => isEnterPress(e) && handleClick(e as any)}
  69. >
  70. {icon}
  71. </span>
  72. );
  73. }