Skip to content

Commit e20709e

Browse files
fix(Foldable): fix issues with content height calculation on resize (#156)
* fix(Foldable): fix issues with content height calculation on resize * fix(HeightCalculator): lint
1 parent 4c6c5b9 commit e20709e

File tree

4 files changed

+56
-14
lines changed

4 files changed

+56
-14
lines changed

src/components/Foldable/Foldable.scss

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
$block: '.#{$ns}foldable-block';
55

66
#{$block} {
7+
height: 0;
8+
overflow-y: hidden;
9+
transition: height $animationDuration, margin-bottom $animationDuration;
10+
711
&__content-container {
8-
height: 0;
9-
overflow-y: hidden;
10-
transition: height $animationDuration, margin-bottom $animationDuration;
12+
overflow: auto;
1113
}
1214
}

src/components/Foldable/Foldable.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import React, {useRef, useState, useCallback, useEffect} from 'react';
1+
import React, {useRef, useEffect} from 'react';
22

33
import {block} from '../../utils';
4-
import HeightCalculator from '../../components/HeightCalculator/HeightCalculator';
54
import {WithChildren} from '../../models';
5+
import useHeightCalculator from '../../hooks/useHeightCalculator';
66

77
import './Foldable.scss';
88

@@ -14,24 +14,21 @@ export interface FoldableProps {
1414
}
1515

1616
const Foldable = ({isOpened, children, className}: WithChildren<FoldableProps>) => {
17+
const blockRef = useRef<HTMLDivElement>(null);
1718
const contentRef = useRef<HTMLDivElement>(null);
18-
const [contentHeight, setContentHeight] = useState<number>();
19-
const onHeightCalculation = useCallback((height: number) => {
20-
setContentHeight(height);
21-
}, []);
19+
const contentHeight = useHeightCalculator(contentRef);
2220

2321
useEffect(() => {
24-
if (contentRef && contentRef.current) {
25-
contentRef.current.style.height = isOpened ? `${contentHeight}px` : '0';
22+
if (blockRef && blockRef.current) {
23+
blockRef.current.style.height = isOpened && contentHeight ? `${contentHeight}px` : '0';
2624
}
2725
}, [isOpened, contentHeight]);
2826

2927
return (
30-
<div className={b(null, className)}>
31-
<div ref={contentRef} className={b('content-container', {open: isOpened})}>
28+
<div ref={blockRef} className={b({open: isOpened}, className)}>
29+
<div ref={contentRef} className={b('content-container')}>
3230
{children}
3331
</div>
34-
<HeightCalculator onCalculate={onHeightCalculation}>{children}</HeightCalculator>
3532
</div>
3633
);
3734
};

src/components/HeightCalculator/HeightCalculator.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export interface HeightCalculatorProps {
1212
onCalculate: (height: number) => void;
1313
}
1414

15+
/**
16+
* @deprecated Will be removed, use the useHeightCalculator hook instead.
17+
* @returns The HeightCalculator component.
18+
*/
1519
const HeightCalculator = ({onCalculate, children}: WithChildren<HeightCalculatorProps>) => {
1620
const [isCalculating, setIsCalculating] = useState(true);
1721
const container = useRef<HTMLDivElement>(null);

src/hooks/useHeightCalculator.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React, {useState, useEffect, useCallback} from 'react';
2+
import _ from 'lodash';
3+
4+
type HeightCalculatorOptions = {
5+
recalculateOnResizeDelay: number;
6+
};
7+
8+
const DEFAULT_RECALCULATE_ON_RESIZE_DELAY = 1000;
9+
const DEFAULT_OPTIONS = {
10+
recalculateOnResizeDelay: DEFAULT_RECALCULATE_ON_RESIZE_DELAY,
11+
};
12+
13+
const useHeightCalculator: (
14+
containerRef: React.RefObject<HTMLElement>,
15+
options?: HeightCalculatorOptions,
16+
) => number | undefined = (containerRef, options = DEFAULT_OPTIONS) => {
17+
const recalculateOnResizeDelay = options.recalculateOnResizeDelay;
18+
const [containerHeight, setContainerHeight] = useState<number | undefined>(undefined);
19+
20+
const calculateContainerHeight = useCallback(() => {
21+
if (containerRef.current && containerRef.current.offsetHeight !== containerHeight)
22+
setContainerHeight(containerRef.current.offsetHeight);
23+
}, [containerRef, containerHeight, setContainerHeight]);
24+
25+
useEffect(() => {
26+
const handleResize = _.debounce(calculateContainerHeight, recalculateOnResizeDelay);
27+
28+
calculateContainerHeight();
29+
30+
window.addEventListener('resize', handleResize);
31+
return () => {
32+
window.removeEventListener('resize', handleResize);
33+
};
34+
}, [calculateContainerHeight, recalculateOnResizeDelay]);
35+
36+
return containerHeight;
37+
};
38+
39+
export default useHeightCalculator;

0 commit comments

Comments
 (0)