| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 | 20x
20x
20x
20x
20x
20x
20x
20x
20x
13x
39x
20x
14x
4x
12x
4x
4x
20x
26x
20x
13x
27x
81x
54x
27x
27x
162x
81x
27x
41x
20x
20x
| import * as classNames from 'classnames';
import * as React from 'react';
import { PureComponent } from 'react';
import { ComponentProps } from '../types';
const ENOUGH_TIME_FOR_RERENDER = 50;
const DEFAULT_HEIGHT = 0;
const DEFAULT_DURATION = 200;
const DEFAULT_FADE_HEIGHT = 50;
const DEFAULT_FADE_COLOR = '#FFF';
export interface CollapseProps extends ComponentProps, React.HTMLAttributes<HTMLDivElement> {
/**
* Whether the collapse is open or not
* @default false
*/
open: boolean;
/**
* Duration of the animation (milliseconds)
* @default 200
*/
animationDuration?: number;
/**
* Maximum height when collapsed
* @default 0
*/
maxCollapsedHeight?: number | string;
/**
* Minimum height
* @default auto
*/
minHeight?: number | string;
/**
* Whether to fade out the content
* @default false
*/
fadeOut?: boolean;
/**
* Color to fade to
* @default white
*/
fadeColor?: string;
/**
* Height of the faded area
* @default 50
*/
fadeHeight?: number;
}
export interface CollapseState { // tslint:disable-line:no-unused-variable
height: number | string;
opened: boolean;
opening: boolean;
}
/**
* Component to expand and collapse content, optionally displaying a small preview.
*/
export class Collapse extends PureComponent<CollapseProps, CollapseState> {
private element: Element;
private timeout: number;
public constructor (props: CollapseProps) {
super(props);
const { maxCollapsedHeight = DEFAULT_HEIGHT, open } = props;
this.state = {
height: maxCollapsedHeight,
opening: false,
opened: open
};
}
public componentDidUpdate (previousProps: CollapseProps) {
if (this.props.open !== previousProps.open) {
window.clearTimeout(this.timeout);
const { maxCollapsedHeight = DEFAULT_HEIGHT, animationDuration = DEFAULT_DURATION } = this.props;
this.setState({
opened: false,
opening: previousProps.open,
height: this.props.open ? maxCollapsedHeight : this.element.scrollHeight
});
this.timeout = window.setTimeout(() => {
this.setState({
opened: false,
opening: this.props.open,
height: this.props.open ? this.element.scrollHeight : maxCollapsedHeight
});
this.timeout = window.setTimeout(() => {
this.setState({
opened: this.props.open,
opening: this.props.open
});
}, animationDuration);
}, ENOUGH_TIME_FOR_RERENDER);
}
}
public componentDidMount () {
const { maxCollapsedHeight = DEFAULT_HEIGHT } = this.props;
this.setState({
height: this.props.open ? this.element.scrollHeight : maxCollapsedHeight
});
}
public componentWillMount () {
window.clearTimeout(this.timeout);
}
public render () {
const {
children,
className,
fadeOut,
fadeColor = DEFAULT_FADE_COLOR,
fadeHeight = DEFAULT_FADE_HEIGHT,
open,
maxCollapsedHeight,
minHeight = null,
animationDuration = DEFAULT_DURATION,
component: Component = 'div',
...remainingProps
} = this.props;
const { opening, opened, height } = this.state;
const collapseStyle = {
minHeight,
maxHeight: opened ? null : height,
position: 'relative' as 'relative',
overflow: 'hidden' as 'hidden',
transition: `ease-in-out ${animationDuration}ms max-height`
};
const fadeStyle = {
height: fadeHeight,
width: '100%',
position: 'absolute' as 'absolute',
bottom: 0,
opacity: opening ? 0 : 1,
background: `linear-gradient(transparent, ${fadeColor} 80%)`,
transition: `ease-in-out ${animationDuration}ms opacity`
};
return (
<Component
ref={(element: HTMLDivElement) => this.element = element}
{...remainingProps}
className={classNames('clearfix collapse', open ? 'collapse-open' : null, className)}
style={collapseStyle}
>
{children}
{
fadeOut && !opened && (
<div
className="collapse-fade"
style={fadeStyle}
/>
)
}
</Component>
);
}
}
export default Collapse;
|