diff --git a/app/components/HoverPreview/Components.tsx b/app/components/HoverPreview/Components.tsx
index 77f6327cf..b37b75b75 100644
--- a/app/components/HoverPreview/Components.tsx
+++ b/app/components/HoverPreview/Components.tsx
@@ -4,7 +4,7 @@ import styled, { css } from "styled-components";
import { s } from "@shared/styles";
import Text from "~/components/Text";
-export const CARD_MARGIN = 16;
+export const CARD_MARGIN = 10;
const NUMBER_OF_LINES = 10;
diff --git a/app/components/HoverPreview/HoverPreview.tsx b/app/components/HoverPreview/HoverPreview.tsx
index d4fb61235..3374fdbe8 100644
--- a/app/components/HoverPreview/HoverPreview.tsx
+++ b/app/components/HoverPreview/HoverPreview.tsx
@@ -2,7 +2,7 @@ import { m } from "framer-motion";
import * as React from "react";
import { Portal } from "react-portal";
import styled from "styled-components";
-import { depths, s } from "@shared/styles";
+import { depths } from "@shared/styles";
import { UnfurlType } from "@shared/types";
import LoadingIndicator from "~/components/LoadingIndicator";
import useEventListener from "~/hooks/useEventListener";
@@ -27,6 +27,14 @@ type Props = {
onClose: () => void;
};
+enum Direction {
+ UP,
+ DOWN,
+}
+
+const POINTER_HEIGHT = 22;
+const POINTER_WIDTH = 22;
+
function HoverPreviewInternal({ element, onClose }: Props) {
const url = element.href || element.dataset.url;
const [isVisible, setVisible] = React.useState(false);
@@ -36,31 +44,46 @@ function HoverPreviewInternal({ element, onClose }: Props) {
const stores = useStores();
const [cardLeft, setCardLeft] = React.useState(0);
const [cardTop, setCardTop] = React.useState(0);
- const [pointerOffset, setPointerOffset] = React.useState(0);
+ const [pointerLeft, setPointerLeft] = React.useState(0);
+ const [pointerTop, setPointerTop] = React.useState(0);
+ const [pointerDir, setPointerDir] = React.useState(Direction.UP);
React.useLayoutEffect(() => {
if (isVisible && cardRef.current) {
const elem = element.getBoundingClientRect();
const card = cardRef.current.getBoundingClientRect();
- const top = elem.bottom + window.scrollY;
- setCardTop(top);
+ let cTop = elem.bottom + window.scrollY + CARD_MARGIN;
+ let pTop = -POINTER_HEIGHT;
+ let pDir = Direction.UP;
+ if (cTop + card.height > window.innerHeight + window.scrollY) {
+ // shift card upwards if it goes out of screen
+ const bottom = elem.top + window.scrollY;
+ cTop = bottom - card.height;
+ // shift a little further to leave some margin between card and element boundary
+ cTop -= CARD_MARGIN;
+ // pointer should be shifted downwards to align with card's bottom
+ pTop = card.height;
+ pDir = Direction.DOWN;
+ }
+ setCardTop(cTop);
+ setPointerTop(pTop);
+ setPointerDir(pDir);
- let left = elem.left;
- let pointerOffset = elem.width / 2;
- if (left + card.width > window.innerWidth) {
+ let cLeft = elem.left;
+ let pLeft = elem.width / 2;
+ if (cLeft + card.width > window.innerWidth) {
// shift card leftwards by the amount it went out of screen
- let shiftBy = left + card.width - window.innerWidth;
- // shift a littler further to leave some margin between card and window boundary
+ let shiftBy = cLeft + card.width - window.innerWidth;
+ // shift a little further to leave some margin between card and window boundary
shiftBy += CARD_MARGIN;
- left -= shiftBy;
+ cLeft -= shiftBy;
// shift pointer rightwards by same amount so as to position it back correctly
- pointerOffset += shiftBy;
+ pLeft += shiftBy;
}
- setCardLeft(left);
-
- setPointerOffset(pointerOffset);
+ setCardLeft(cLeft);
+ setPointerLeft(pLeft);
}
}, [isVisible, element]);
@@ -193,7 +216,11 @@ function HoverPreviewInternal({ element, onClose }: Props) {
description={data.description}
/>
)}
-
+
) : null}
@@ -217,7 +244,6 @@ const Animate = styled(m.div)`
`;
const Position = styled.div<{ fixed?: boolean; top?: number; left?: number }>`
- margin-top: 10px;
position: ${({ fixed }) => (fixed ? "fixed" : "absolute")};
z-index: ${depths.hoverPreview};
display: flex;
@@ -227,11 +253,11 @@ const Position = styled.div<{ fixed?: boolean; top?: number; left?: number }>`
${({ left }) => (left !== undefined ? `left: ${left}px` : "")};
`;
-const Pointer = styled.div<{ offset: number }>`
- top: -22px;
- left: ${(props) => props.offset}px;
- width: 22px;
- height: 22px;
+const Pointer = styled.div<{ top: number; left: number; direction: Direction }>`
+ top: ${(props) => props.top}px;
+ left: ${(props) => props.left}px;
+ width: ${POINTER_WIDTH}px;
+ height: ${POINTER_HEIGHT}px;
position: absolute;
transform: translateX(-50%);
pointer-events: none;
@@ -241,20 +267,26 @@ const Pointer = styled.div<{ offset: number }>`
content: "";
display: inline-block;
position: absolute;
- bottom: 0;
- right: 0;
+ ${({ direction }) => (direction === Direction.UP ? "bottom: 0" : "top: 0")};
+ ${({ direction }) => (direction === Direction.UP ? "right: 0" : "left: 0")};
}
&:before {
border: 8px solid transparent;
- border-bottom-color: ${(props) =>
- props.theme.menuBorder || "rgba(0, 0, 0, 0.1)"};
- right: -1px;
+ ${({ direction, theme }) =>
+ direction === Direction.UP
+ ? `border-bottom-color: ${theme.menuBorder || "rgba(0, 0, 0, 0.1)"}`
+ : `border-top-color: ${theme.menuBorder || "rgba(0, 0, 0, 0.1)"}`};
+ ${({ direction }) =>
+ direction === Direction.UP ? "right: -1px" : "left: -1px"};
}
&:after {
border: 7px solid transparent;
- border-bottom-color: ${s("menuBackground")};
+ ${({ direction, theme }) =>
+ direction === Direction.UP
+ ? `border-bottom-color: ${theme.menuBackground}`
+ : `border-top-color: ${theme.menuBackground}`};
}
`;
diff --git a/app/components/HoverPreview/HoverPreviewLink.tsx b/app/components/HoverPreview/HoverPreviewLink.tsx
index 1fbdbaa31..c1f429f4b 100644
--- a/app/components/HoverPreview/HoverPreviewLink.tsx
+++ b/app/components/HoverPreview/HoverPreviewLink.tsx
@@ -26,9 +26,9 @@ const HoverPreviewLink = React.forwardRef(function _HoverPreviewLink(
) {
return (
-
+
{thumbnailUrl ? : null}
-
+
{title}