|
37 | 37 |
|
38 | 38 | $effect(() => { |
39 | 39 | let tracking = true; |
| 40 | +
|
40 | 41 | const track = () => { |
41 | 42 | if (nonNullish(anchorRef) && nonNullish(popoverRef)) { |
42 | 43 | const anchorRect = anchorRef.getBoundingClientRect(); |
43 | 44 | const popoverRect = popoverRef.getBoundingClientRect(); |
44 | 45 |
|
| 46 | + // Available space around the anchor |
| 47 | + const spaceAbove = anchorRect.top; |
| 48 | + const spaceBelow = window.innerHeight - anchorRect.bottom; |
| 49 | + const spaceLeft = anchorRect.left; |
| 50 | + const spaceRight = window.innerWidth - anchorRect.right; |
| 51 | +
|
| 52 | + // Determine flipped direction if needed |
| 53 | + let finalDirection = direction; |
| 54 | +
|
| 55 | + // Vertical flip |
| 56 | + if ( |
| 57 | + direction === "down" && |
| 58 | + spaceBelow < popoverRect.height && |
| 59 | + spaceAbove > spaceBelow |
| 60 | + ) { |
| 61 | + finalDirection = "up"; |
| 62 | + } else if ( |
| 63 | + direction === "up" && |
| 64 | + spaceAbove < popoverRect.height && |
| 65 | + spaceBelow > spaceAbove |
| 66 | + ) { |
| 67 | + finalDirection = "down"; |
| 68 | + } |
| 69 | +
|
| 70 | + // Horizontal flip |
| 71 | + if ( |
| 72 | + direction === "right" && |
| 73 | + spaceRight < popoverRect.width && |
| 74 | + spaceLeft > spaceRight |
| 75 | + ) { |
| 76 | + finalDirection = "left"; |
| 77 | + } else if ( |
| 78 | + direction === "left" && |
| 79 | + spaceLeft < popoverRect.width && |
| 80 | + spaceRight > spaceLeft |
| 81 | + ) { |
| 82 | + finalDirection = "right"; |
| 83 | + } |
| 84 | +
|
| 85 | + // Compute top position |
45 | 86 | popoverRef.style.top = { |
46 | 87 | up: `calc(${anchorRect.top - popoverRect.height}px - ${distance})`, |
47 | | - right: { |
| 88 | + down: `calc(${anchorRect.bottom}px + ${distance})`, |
| 89 | + left: { |
48 | 90 | start: `${anchorRect.top}px`, |
49 | 91 | center: `${anchorRect.top + anchorRect.height * 0.5 - popoverRect.height * 0.5}px`, |
50 | 92 | end: `${anchorRect.bottom - popoverRect.height}px`, |
51 | 93 | }[align], |
52 | | - down: `calc(${anchorRect.bottom}px + ${distance})`, |
53 | | - left: { |
| 94 | + right: { |
54 | 95 | start: `${anchorRect.top}px`, |
55 | 96 | center: `${anchorRect.top + anchorRect.height * 0.5 - popoverRect.height * 0.5}px`, |
56 | 97 | end: `${anchorRect.bottom - popoverRect.height}px`, |
57 | 98 | }[align], |
58 | | - }[direction]; |
| 99 | + }[finalDirection]; |
59 | 100 |
|
| 101 | + // Compute left position |
60 | 102 | popoverRef.style.left = { |
61 | 103 | up: { |
62 | 104 | start: `${anchorRect.left}px`, |
63 | 105 | center: `${anchorRect.left + anchorRect.width * 0.5 - popoverRect.width * 0.5}px`, |
64 | 106 | end: `${anchorRect.right - popoverRect.width}px`, |
65 | 107 | }[align], |
66 | | - right: `calc(${anchorRect.right}px + ${distance})`, |
67 | 108 | down: { |
68 | 109 | start: `${anchorRect.left}px`, |
69 | 110 | center: `${anchorRect.left + anchorRect.width * 0.5 - popoverRect.width * 0.5}px`, |
70 | 111 | end: `${anchorRect.right - popoverRect.width}px`, |
71 | 112 | }[align], |
72 | 113 | left: `calc(${anchorRect.left - popoverRect.width}px - ${distance})`, |
73 | | - }[direction]; |
74 | | - } |
75 | | - if (tracking) { |
76 | | - requestAnimationFrame(track); |
| 114 | + right: `calc(${anchorRect.right}px + ${distance})`, |
| 115 | + }[finalDirection]; |
77 | 116 | } |
| 117 | +
|
| 118 | + if (tracking) requestAnimationFrame(track); |
78 | 119 | }; |
| 120 | +
|
79 | 121 | requestAnimationFrame(track); |
| 122 | +
|
80 | 123 | return () => { |
81 | 124 | tracking = false; |
82 | 125 | }; |
|
0 commit comments