forked from mantinedev/mantine
/
Spoiler.tsx
94 lines (78 loc) · 2.63 KB
/
Spoiler.tsx
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
import React, { useState, useEffect, forwardRef } from 'react';
import { DefaultProps, Selectors, useComponentDefaultProps } from '@mantine/styles';
import { useElementSize } from '@mantine/hooks';
import { Anchor } from '../Anchor';
import { Box } from '../Box';
import useStyles, { SpoilerStylesParams } from './Spoiler.styles';
export type SpoilerStylesNames = Selectors<typeof useStyles>;
export interface SpoilerProps
extends DefaultProps<SpoilerStylesNames, SpoilerStylesParams>,
React.ComponentPropsWithoutRef<'div'> {
/** Max height of visible content, when this point is reached spoiler appears */
maxHeight: number;
/** Label for close spoiler action */
hideLabel: React.ReactNode;
/** Label for open spoiler action */
showLabel: React.ReactNode;
/** Get ref of spoiler toggle button */
controlRef?: React.ForwardedRef<HTMLButtonElement>;
/** Initial spoiler state, true to wrap content in spoiler, false to show content without spoiler, opened state will be updated on mount */
initialState?: boolean;
/** Spoiler reveal transition duration in ms, 0 or null to turn off animation */
transitionDuration?: number;
}
const defaultProps: Partial<SpoilerProps> = {
maxHeight: 100,
transitionDuration: 200,
initialState: false,
};
export const Spoiler = forwardRef<HTMLDivElement, SpoilerProps>((props, ref) => {
const {
className,
children,
maxHeight,
hideLabel,
showLabel,
transitionDuration,
controlRef,
initialState,
classNames,
styles,
unstyled,
...others
} = useComponentDefaultProps('Spoiler', defaultProps, props);
const { classes, cx } = useStyles(
{ transitionDuration },
{ classNames, styles, unstyled, name: 'Spoiler' }
);
const [show, setShowState] = useState(initialState);
const [spoiler, setSpoilerState] = useState(initialState);
const { ref: contentRef, height } = useElementSize();
const spoilerMoreContent = show ? hideLabel : showLabel;
useEffect(() => {
setSpoilerState(maxHeight < height);
}, [height, maxHeight, children]);
return (
<Box className={cx(classes.root, className)} ref={ref} {...others}>
<div
className={classes.content}
style={{
maxHeight: !show ? maxHeight : height || undefined,
}}
>
<div ref={contentRef}>{children}</div>
</div>
{spoiler && (
<Anchor
component="button"
ref={controlRef}
onClick={() => setShowState((opened) => !opened)}
className={classes.control}
>
{spoilerMoreContent}
</Anchor>
)}
</Box>
);
});
Spoiler.displayName = '@mantine/core/Spoiler';