forked from mantinedev/mantine
/
use-popover.ts
103 lines (90 loc) · 2.32 KB
/
use-popover.ts
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
import { useDidUpdate, useUncontrolled } from '@mantine/hooks';
import {
useFloating,
shift,
flip,
arrow,
offset,
size,
Middleware,
} from '@floating-ui/react-dom-interactions';
import { FloatingPosition, useFloatingAutoUpdate } from '../Floating';
import { PopoverWidth, PopoverMiddlewares } from './Popover.types';
interface UsePopoverOptions {
offset: number;
position: FloatingPosition;
positionDependencies: any[];
onPositionChange?(position: FloatingPosition): void;
opened: boolean;
defaultOpened: boolean;
onChange(opened: boolean): void;
onClose?(): void;
onOpen?(): void;
width: PopoverWidth;
middlewares: PopoverMiddlewares;
arrowRef: React.RefObject<HTMLDivElement>;
}
function getPopoverMiddlewares(options: UsePopoverOptions) {
const middlewares: Middleware[] = [offset(options.offset)];
if (options.middlewares.shift) {
middlewares.push(shift());
}
if (options.middlewares.flip) {
middlewares.push(flip());
}
middlewares.push(arrow({ element: options.arrowRef }));
return middlewares;
}
export function usePopover(options: UsePopoverOptions) {
const [_opened, setOpened] = useUncontrolled({
value: options.opened,
defaultValue: options.defaultOpened,
finalValue: false,
onChange: options.onChange,
});
const onClose = () => {
options.onClose?.();
setOpened(false);
};
const onToggle = () => {
if (_opened) {
options.onClose?.();
setOpened(false);
} else {
options.onOpen?.();
setOpened(true);
}
};
const floating = useFloating({
placement: options.position,
middleware: [
...getPopoverMiddlewares(options),
...(options.width === 'target'
? [
size({
apply({ rects }) {
Object.assign(floating.refs.floating.current?.style ?? {}, {
width: `${rects.reference.width}px`,
});
},
}),
]
: []),
],
});
useFloatingAutoUpdate({
opened: options.opened,
positionDependencies: options.positionDependencies,
floating,
});
useDidUpdate(() => {
options.onPositionChange?.(floating.placement);
}, [floating.placement]);
return {
floating,
controlled: typeof options.opened === 'boolean',
opened: _opened,
onClose,
onToggle,
};
}