autoSizer.tsx 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import React, { PureComponent } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { cssClasses } from '@douyinfe/semi-foundation/tree/constants';
  4. import ResizeObserver, { ResizeEntry } from '../resizeObserver';
  5. import { get } from 'lodash';
  6. export interface AutoSizerProps {
  7. defaultHeight?: number | string;
  8. defaultWidth?: number | string;
  9. children?: (info: { width: string | number; height: string | number }) => React.ReactNode;
  10. }
  11. export interface AutoSizerState {
  12. height: number | string;
  13. }
  14. const prefixcls = cssClasses.PREFIX;
  15. export default class AutoSizer extends PureComponent<AutoSizerProps, AutoSizerState> {
  16. static propTypes = {
  17. defaultHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  18. defaultWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  19. };
  20. static defaultProps = {
  21. defaultHeight: '100%',
  22. defaultWidth: '100%',
  23. };
  24. constructor(props: AutoSizerProps) {
  25. super(props);
  26. this.state = {
  27. height: this.props.defaultHeight || 0,
  28. };
  29. }
  30. componentDidMount() {
  31. const { height } = this.state;
  32. // if height is a number, pass it directly to virtual-list
  33. if (typeof height === 'number') {
  34. return;
  35. }
  36. }
  37. _onResize = (entries: ResizeEntry[]) => {
  38. // observe parent node height
  39. const target = entries && entries[1] && entries[1].target;
  40. if (target) {
  41. const height = get(target, 'offsetHeight') || 0;
  42. const style = window.getComputedStyle(target) || {};
  43. const paddingTop = parseInt(get(style, 'paddingTop'), 10) || 0;
  44. const paddingBottom = parseInt(get(style, 'paddingBottom'), 10) || 0;
  45. const newHeight = height - paddingTop - paddingBottom;
  46. if (this.state.height !== newHeight) {
  47. this.setState({
  48. height: height - paddingTop - paddingBottom,
  49. });
  50. }
  51. }
  52. };
  53. render() {
  54. const { children, defaultWidth, defaultHeight } = this.props;
  55. const { height } = this.state;
  56. // Avoid rendering children before the initial measurements have been collected.
  57. // At best this would just be wasting cycles. Refer to https://github.com/bvaughn/react-virtualized-auto-sizer/
  58. let bailoutOnChildren = false;
  59. if (height === 0 || typeof height !== 'number') {
  60. bailoutOnChildren = true;
  61. }
  62. return (
  63. <ResizeObserver observeParent onResize={this._onResize}>
  64. <div
  65. style={{
  66. height: defaultHeight,
  67. overflow: 'visible',
  68. }}
  69. className={`${prefixcls}-auto-wrapper`}
  70. >
  71. {!bailoutOnChildren && children({ height, width: defaultWidth })}
  72. </div>
  73. </ResizeObserver>
  74. );
  75. }
  76. }