1 | import React, { useRef, useEffect, useState } from 'react';
|
2 | import { useSpring, animated, to } from '@react-spring/web';
|
3 | import { useDrag } from '@use-gesture/react';
|
4 | import { mergeProps } from '../../utils/with-default-props';
|
5 | import { withNativeProps } from '../../utils/native-props';
|
6 | const classPrefix = `adm-floating-bubble`;
|
7 | const defaultProps = {
|
8 | axis: 'y',
|
9 | defaultOffset: {
|
10 | x: 0,
|
11 | y: 0
|
12 | }
|
13 | };
|
14 | export const FloatingBubble = p => {
|
15 | const props = mergeProps(defaultProps, p);
|
16 | const boundaryRef = useRef(null);
|
17 | const buttonRef = useRef(null);
|
18 | const [innerValue, setInnerValue] = useState(props.offset === undefined ? props.defaultOffset : props.offset);
|
19 | useEffect(() => {
|
20 | if (props.offset === undefined) return;
|
21 | api.start({
|
22 | x: props.offset.x,
|
23 | y: props.offset.y
|
24 | });
|
25 | }, [props.offset]);
|
26 | |
27 |
|
28 |
|
29 |
|
30 |
|
31 | const [{
|
32 | x,
|
33 | y,
|
34 | opacity
|
35 | }, api] = useSpring(() => ({
|
36 | x: innerValue.x,
|
37 | y: innerValue.y,
|
38 | opacity: 1
|
39 | }));
|
40 | const bind = useDrag(state => {
|
41 | var _a;
|
42 | let nextX = state.offset[0];
|
43 | let nextY = state.offset[1];
|
44 | if (state.last && props.magnetic) {
|
45 | const boundary = boundaryRef.current;
|
46 | const button = buttonRef.current;
|
47 | if (!boundary || !button) return;
|
48 | const boundaryRect = boundary.getBoundingClientRect();
|
49 | const buttonRect = button.getBoundingClientRect();
|
50 | if (props.magnetic === 'x') {
|
51 | const compensation = x.goal - x.get();
|
52 | const leftDistance = buttonRect.left + compensation - boundaryRect.left;
|
53 | const rightDistance = boundaryRect.right - (buttonRect.right + compensation);
|
54 | if (rightDistance <= leftDistance) {
|
55 | nextX += rightDistance;
|
56 | } else {
|
57 | nextX -= leftDistance;
|
58 | }
|
59 | } else if (props.magnetic === 'y') {
|
60 | const compensation = y.goal - y.get();
|
61 | const topDistance = buttonRect.top + compensation - boundaryRect.top;
|
62 | const bottomDistance = boundaryRect.bottom - (buttonRect.bottom + compensation);
|
63 | if (bottomDistance <= topDistance) {
|
64 | nextY += bottomDistance;
|
65 | } else {
|
66 | nextY -= topDistance;
|
67 | }
|
68 | }
|
69 | }
|
70 | const nextOffest = {
|
71 | x: nextX,
|
72 | y: nextY
|
73 | };
|
74 | if (props.offset === undefined) {
|
75 |
|
76 | api.start(nextOffest);
|
77 | } else {
|
78 | setInnerValue(nextOffest);
|
79 | }
|
80 | (_a = props.onOffsetChange) === null || _a === void 0 ? void 0 : _a.call(props, nextOffest);
|
81 |
|
82 | api.start({
|
83 | opacity: state.active ? 0.8 : 1
|
84 | });
|
85 | }, {
|
86 | axis: props.axis === 'xy' ? undefined : props.axis,
|
87 | pointer: {
|
88 | touch: true
|
89 | },
|
90 |
|
91 | filterTaps: true,
|
92 |
|
93 | bounds: boundaryRef,
|
94 | from: () => [x.get(), y.get()]
|
95 | });
|
96 | return withNativeProps(props, React.createElement("div", {
|
97 | className: classPrefix
|
98 | }, React.createElement("div", {
|
99 | className: `${classPrefix}-boundary-outer`
|
100 | }, React.createElement("div", {
|
101 | className: `${classPrefix}-boundary`,
|
102 | ref: boundaryRef
|
103 | })), React.createElement(animated.div, Object.assign({}, bind(), {
|
104 | style: {
|
105 | opacity,
|
106 | transform: to([x, y], (x, y) => `translate(${x}px, ${y}px)`)
|
107 | },
|
108 | onClick: props.onClick,
|
109 | className: `${classPrefix}-button`,
|
110 | ref: buttonRef
|
111 | }), props.children)));
|
112 | }; |
\ | No newline at end of file |