Skip to content

Commit 9a201c9

Browse files
committed
Fixed tooltip, added animations
1 parent 0e2b986 commit 9a201c9

File tree

7 files changed

+146
-83
lines changed

7 files changed

+146
-83
lines changed

src/components/Overlay/OverlayTrigger.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useRef, useState } from 'react';
44
import Overlay from '@/components/Overlay/index';
55
import { Placement, PositioningStrategy } from '@popperjs/core';
66
import { Trigger, triggerPropTypes } from '@/components/Overlay/Trigger';
7+
import { AnimatePresence } from 'framer-motion';
78

89
interface OverlayTriggerProps {
910
arrow?: boolean;
@@ -13,6 +14,7 @@ interface OverlayTriggerProps {
1314
positionStrategy?: PositioningStrategy;
1415
className?: string;
1516
trigger?: Trigger | string;
17+
motion?: string;
1618
}
1719

1820
const OverlayTrigger = ({
@@ -22,7 +24,8 @@ const OverlayTrigger = ({
2224
overlay,
2325
placement,
2426
positionStrategy,
25-
trigger = 'hover'
27+
trigger = 'hover',
28+
motion
2629
}: OverlayTriggerProps): React.ReactElement => {
2730
const [shown, setShown] = useState<boolean>(false);
2831
const triggerRef = useRef<HTMLElement>();
@@ -66,17 +69,20 @@ const OverlayTrigger = ({
6669
ref: triggerRef,
6770
...attachEvents(triggerElement, trigger)
6871
})}
69-
{shown && (
70-
<Overlay
71-
arrow={arrow}
72-
triggerRef={triggerRef}
73-
placement={placement}
74-
positionStrategy={positionStrategy}
75-
className={className}
76-
>
77-
{overlay}
78-
</Overlay>
79-
)}
72+
<AnimatePresence>
73+
{shown && (
74+
<Overlay
75+
motion={motion}
76+
arrow={arrow}
77+
triggerRef={triggerRef}
78+
placement={placement}
79+
positionStrategy={positionStrategy}
80+
className={className}
81+
>
82+
{overlay}
83+
</Overlay>
84+
)}
85+
</AnimatePresence>
8086
</>
8187
)
8288
}

src/components/Overlay/index.tsx

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as React from 'react';
1+
import React from 'react';
22
import { createPortal } from 'react-dom';
33
import { useEffect, useRef } from 'react';
44
import {
@@ -10,14 +10,16 @@ import {
1010
import PropTypes from 'prop-types';
1111
import { Modifier } from '@popperjs/core/lib/types';
1212
import clsx from 'clsx';
13-
import { motion } from 'framer-motion';
13+
import { AnimationFeature, ExitFeature, HTMLMotionProps, m as motion, MotionConfig } from 'framer-motion';
14+
import { motionsMap } from '@/components/animations/motionsMap';
1415

1516
interface OverlayProps {
1617
className?: string;
1718
triggerRef: React.MutableRefObject<HTMLElement | undefined>;
1819
placement?: Placement;
1920
arrow?: boolean;
2021
positionStrategy?: PositioningStrategy;
22+
motion?: string;
2123
}
2224

2325
const Overlay = ({
@@ -26,7 +28,8 @@ const Overlay = ({
2628
triggerRef,
2729
placement = 'top',
2830
arrow = true,
29-
positionStrategy = 'absolute'
31+
positionStrategy = 'absolute',
32+
motion: triggerMotion
3033
}: React.PropsWithChildren<OverlayProps>): React.ReactElement => {
3134
const ref = useRef<HTMLDivElement | null>(null);
3235
const popper = useRef<PopperInstance>();
@@ -44,9 +47,21 @@ const Overlay = ({
4447
modifiers: createModifiers(),
4548
placement,
4649
strategy: positionStrategy
47-
})
50+
});
4851

49-
useEffect(() => {
52+
const createMotion = (): Record<string, HTMLMotionProps<'div'>> => {
53+
if (!triggerMotion) {
54+
return {};
55+
}
56+
57+
if (Object.prototype.hasOwnProperty.call(motionsMap, triggerMotion)) {
58+
return motionsMap[triggerMotion];
59+
}
60+
61+
return {};
62+
}
63+
64+
useEffect((): void => {
5065
if (ref.current && triggerRef.current) {
5166
popper.current = createPopper(
5267
triggerRef.current,
@@ -58,21 +73,29 @@ const Overlay = ({
5873
}, [])
5974

6075
return createPortal(
61-
<div
62-
ref={ref}
63-
className={clsx(
64-
'overlay-container',
65-
arrow && 'has-arrow',
66-
className
67-
)}
68-
>
69-
{arrow && (<div className="overlay-arrow arrow" />)}
70-
<motion.div
71-
className="content"
76+
<MotionConfig features={[ExitFeature, AnimationFeature]}>
77+
<div
78+
ref={ref}
79+
className={clsx(
80+
'overlay-container',
81+
arrow && 'has-arrow',
82+
className
83+
)}
7284
>
73-
{children}
74-
</motion.div>
75-
</div> ,
85+
<motion.div
86+
className="overlay-animator"
87+
exit={{}}
88+
{...createMotion()}
89+
>
90+
{arrow && (<div className="overlay-arrow arrow" />)}
91+
<div
92+
className="content"
93+
>
94+
{children}
95+
</div>
96+
</motion.div>
97+
</div>
98+
</MotionConfig>,
7699
document.body
77100
)
78101
}

src/components/Tooltip/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@ interface TooltipProps {
1010
placement?: Placement;
1111
content: React.ReactNode;
1212
trigger?: Trigger | string;
13+
motion?: string;
1314
}
1415

1516
const Tooltip = ({
1617
arrow,
1718
children,
1819
content,
1920
placement,
20-
trigger = 'hover'
21+
trigger = 'hover',
22+
motion
2123
}: TooltipProps): React.ReactElement => (
2224
<OverlayTrigger
25+
motion={motion}
2326
trigger={trigger}
2427
arrow={arrow}
2528
overlay={content}

src/components/animations/fade.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
initial: { opacity: 0},
3+
animate: { opacity: 1 },
4+
exit: { opacity: 0 },
5+
transition: { duration: 0.15 },
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import PropTypes from 'prop-types';
2+
import fade from '@/components/animations/fade';
3+
4+
export const motionsMap = {
5+
fade
6+
}
7+
8+
export const motionsPropTypes = PropTypes.oneOf(['fade'])

src/style/components/_tooltips.scss

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,106 @@
11
.tooltip {
2-
> .content {
3-
background: rgba(0, 0, 0, .7);
4-
color: #fff;
5-
padding: calc(var(--base-gutter) / 2);
6-
border-radius: var(--base-border-radius);
2+
> .overlay-animator {
3+
position: relative;
74
}
85

9-
> .arrow {
10-
position: absolute;
11-
z-index: -1;
6+
> .overlay-animator, & {
7+
> .content {
8+
background: rgba(0, 0, 0, .7);
9+
color: #fff;
10+
padding: calc(var(--base-gutter) / 2);
11+
border-radius: var(--base-border-radius);
12+
}
1213

13-
&:before {
14-
content: '';
14+
> .arrow {
1515
position: absolute;
16+
z-index: -1;
17+
18+
&:before {
19+
content: '';
20+
position: absolute;
21+
}
1622
}
1723
}
1824

1925
&[data-popper-placement^="top"] {
2026
&.has-arrow {
21-
> .content {
22-
margin-bottom: 8px;
23-
}
27+
> .overlay-animator, & {
28+
> .content {
29+
margin-bottom: 8px;
30+
}
2431

25-
> .arrow {
26-
bottom: 0;
27-
height: 8px;
28-
width: 16px;
32+
> .arrow {
33+
bottom: -8px;
34+
height: 8px;
35+
width: 16px;
2936

30-
&:before {
31-
border-top: solid 8px rgba(0, 0, 0, .7);
32-
border-left: solid 8px transparent;
33-
border-right: solid 8px transparent;
37+
&:before {
38+
border-top: solid 8px rgba(0, 0, 0, .7);
39+
border-left: solid 8px transparent;
40+
border-right: solid 8px transparent;
41+
}
3442
}
3543
}
3644
}
3745
}
3846

3947
&[data-popper-placement^="bottom"] {
4048
&.has-arrow {
41-
> .content {
42-
margin-top: 8px;
43-
}
49+
> .overlay-animator, & {
50+
> .content {
51+
margin-top: 8px;
52+
}
4453

45-
> .arrow {
46-
top: 0;
47-
height: 8px;
48-
width: 16px;
54+
> .arrow {
55+
top: 0;
56+
height: 8px;
57+
width: 16px;
4958

50-
&:before {
51-
border-bottom: solid 8px rgba(0, 0, 0, .7);
52-
border-left: solid 8px transparent;
53-
border-right: solid 8px transparent;
59+
&:before {
60+
border-bottom: solid 8px rgba(0, 0, 0, .7);
61+
border-left: solid 8px transparent;
62+
border-right: solid 8px transparent;
63+
}
5464
}
5565
}
5666
}
5767
}
5868

5969
&[data-popper-placement^="right"] {
6070
&.has-arrow {
61-
> .content {
62-
margin-left: 8px;
63-
}
71+
> .overlay-animator, & {
72+
> .content {
73+
margin-left: 8px;
74+
}
6475

65-
> .arrow {
66-
left: 0;
67-
height: 16px;
68-
width: 8px;
76+
> .arrow {
77+
left: 0;
78+
height: 16px;
79+
width: 8px;
6980

70-
&:before {
71-
border-right: solid 8px rgba(0, 0, 0, .7);
72-
border-top: solid 8px transparent;
73-
border-bottom: solid 8px transparent;
81+
&:before {
82+
border-right: solid 8px rgba(0, 0, 0, .7);
83+
border-top: solid 8px transparent;
84+
border-bottom: solid 8px transparent;
85+
}
7486
}
7587
}
7688
}
7789
}
7890

7991
&[data-popper-placement^="left"] {
8092
&.has-arrow {
81-
> .content {
82-
margin-right: 8px;
83-
}
93+
> .overlay-animator, & {
94+
> .content {
95+
margin-right: 8px;
96+
}
8497

85-
> .arrow {
86-
right: 0;
87-
border-bottom: solid 8px transparent;
88-
border-left: solid 8px rgba(0, 0, 0, .7);
89-
border-top: solid 8px transparent;
98+
> .arrow {
99+
right: -8px;
100+
border-bottom: solid 8px transparent;
101+
border-left: solid 8px rgba(0, 0, 0, .7);
102+
border-top: solid 8px transparent;
103+
}
90104
}
91105
}
92106
}

www/src/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ ReactDom.render(
123123
content="Megalange tekst"
124124
placement="top"
125125
trigger="click"
126+
motion="fade"
126127
>
127128
<Button
128129
variant={Variant.ORANGE}
@@ -133,6 +134,8 @@ ReactDom.render(
133134
<Tooltip
134135
content="Megalange tekst"
135136
placement="right"
137+
trigger="click"
138+
motion="fade"
136139
>
137140
<Button
138141
variant={Variant.ORANGE}

0 commit comments

Comments
 (0)