diff --git a/app/components/HoverPreview/Components.tsx b/app/components/HoverPreview/Components.tsx index 55ee05abb..77f6327cf 100644 --- a/app/components/HoverPreview/Components.tsx +++ b/app/components/HoverPreview/Components.tsx @@ -4,11 +4,7 @@ import styled, { css } from "styled-components"; import { s } from "@shared/styles"; import Text from "~/components/Text"; -export const CARD_PADDING = 16; - -export const CARD_WIDTH = 375; - -export const THUMBNAIL_HEIGHT = 200; +export const CARD_MARGIN = 16; const NUMBER_OF_LINES = 10; @@ -28,6 +24,8 @@ export const Preview = styled(Link)` 0 0 1px 1px rgba(0, 0, 0, 0.05); overflow: hidden; position: absolute; + min-width: 350px; + max-width: 375px; `; export const Title = styled.h2` @@ -39,7 +37,9 @@ export const Title = styled.h2` export const Info = styled(StyledText).attrs(() => ({ type: "tertiary", size: "xsmall", -}))``; +}))` + white-space: nowrap; +`; export const Description = styled(StyledText)` ${sharedVars} @@ -48,6 +48,12 @@ export const Description = styled(StyledText)` max-height: calc(var(--line-height) * ${NUMBER_OF_LINES}); `; +export const Thumbnail = styled.img` + object-fit: cover; + height: 200px; + background: ${s("menuBackground")}; +`; + export const CardContent = styled.div` overflow: hidden; user-select: none; @@ -57,8 +63,7 @@ export const CardContent = styled.div` export const Card = styled.div<{ fadeOut?: boolean; $borderRadius?: string }>` backdrop-filter: blur(10px); background: ${s("menuBackground")}; - padding: ${CARD_PADDING}px; - width: ${CARD_WIDTH}px; + padding: 16px; font-size: 0.9em; position: relative; diff --git a/app/components/HoverPreview/HoverPreview.tsx b/app/components/HoverPreview/HoverPreview.tsx index 7326f989d..d4fb61235 100644 --- a/app/components/HoverPreview/HoverPreview.tsx +++ b/app/components/HoverPreview/HoverPreview.tsx @@ -12,7 +12,7 @@ import useOnClickOutside from "~/hooks/useOnClickOutside"; import useRequest from "~/hooks/useRequest"; import useStores from "~/hooks/useStores"; import { client } from "~/utils/ApiClient"; -import { CARD_WIDTH, CARD_PADDING } from "./Components"; +import { CARD_MARGIN } from "./Components"; import HoverPreviewDocument from "./HoverPreviewDocument"; import HoverPreviewLink from "./HoverPreviewLink"; import HoverPreviewMention from "./HoverPreviewMention"; @@ -34,6 +34,35 @@ function HoverPreviewInternal({ element, onClose }: Props) { const timerOpen = React.useRef>(); const cardRef = React.useRef(null); const stores = useStores(); + const [cardLeft, setCardLeft] = React.useState(0); + const [cardTop, setCardTop] = React.useState(0); + const [pointerOffset, setPointerOffset] = React.useState(0); + + React.useLayoutEffect(() => { + if (isVisible && cardRef.current) { + const elem = element.getBoundingClientRect(); + const card = cardRef.current.getBoundingClientRect(); + + const top = elem.bottom + window.scrollY; + setCardTop(top); + + let left = elem.left; + let pointerOffset = elem.width / 2; + if (left + 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 + shiftBy += CARD_MARGIN; + left -= shiftBy; + + // shift pointer rightwards by same amount so as to position it back correctly + pointerOffset += shiftBy; + } + setCardLeft(left); + + setPointerOffset(pointerOffset); + } + }, [isVisible, element]); const { data, request, loading } = useRequest( React.useCallback( @@ -122,13 +151,6 @@ function HoverPreviewInternal({ element, onClose }: Props) { }; }, [element, startCloseTimer, data]); - const elemBounds = element.getBoundingClientRect(); - const cardBounds = cardRef.current?.getBoundingClientRect(); - const left = cardBounds - ? Math.min(elemBounds.left, window.innerWidth - CARD_PADDING - CARD_WIDTH) - : elemBounds.left; - const leftOffset = elemBounds.left - left; - if (loading) { return ; } @@ -139,44 +161,41 @@ function HoverPreviewInternal({ element, onClose }: Props) { return ( - -
- {isVisible ? ( - - {data.type === UnfurlType.Mention ? ( - - ) : data.type === UnfurlType.Document ? ( - - ) : ( - - )} - - - ) : null} -
+ + {isVisible ? ( + + {data.type === UnfurlType.Mention ? ( + + ) : data.type === UnfurlType.Document ? ( + + ) : ( + + )} + + + ) : null}
); diff --git a/app/components/HoverPreview/HoverPreviewDocument.tsx b/app/components/HoverPreview/HoverPreviewDocument.tsx index 749c617f1..b84871901 100644 --- a/app/components/HoverPreview/HoverPreviewDocument.tsx +++ b/app/components/HoverPreview/HoverPreviewDocument.tsx @@ -23,10 +23,13 @@ type Props = { description: string; }; -function HoverPreviewDocument({ id, url, title, info, description }: Props) { +const HoverPreviewDocument = React.forwardRef(function _HoverPreviewDocument( + { id, url, title, info, description }: Props, + ref: React.Ref +) { return ( - + {title} @@ -46,6 +49,6 @@ function HoverPreviewDocument({ id, url, title, info, description }: Props) { ); -} +}); export default HoverPreviewDocument; diff --git a/app/components/HoverPreview/HoverPreviewLink.tsx b/app/components/HoverPreview/HoverPreviewLink.tsx index 41fa4a1ab..1fbdbaa31 100644 --- a/app/components/HoverPreview/HoverPreviewLink.tsx +++ b/app/components/HoverPreview/HoverPreviewLink.tsx @@ -1,6 +1,4 @@ import * as React from "react"; -import styled from "styled-components"; -import { s } from "@shared/styles"; import Flex from "~/components/Flex"; import { Preview, @@ -8,8 +6,7 @@ import { Description, Card, CardContent, - CARD_WIDTH, - THUMBNAIL_HEIGHT, + Thumbnail, } from "./Components"; type Props = { @@ -23,12 +20,15 @@ type Props = { description: string; }; -function HoverPreviewLink({ url, thumbnailUrl, title, description }: Props) { +const HoverPreviewLink = React.forwardRef(function _HoverPreviewLink( + { url, thumbnailUrl, title, description }: Props, + ref: React.Ref +) { return ( {thumbnailUrl ? : null} - + {title} @@ -39,13 +39,6 @@ function HoverPreviewLink({ url, thumbnailUrl, title, description }: Props) { ); -} - -const Thumbnail = styled.img` - object-fit: cover; - max-width: ${CARD_WIDTH}px; - height: ${THUMBNAIL_HEIGHT}px; - background: ${s("menuBackground")}; -`; +}); export default HoverPreviewLink; diff --git a/app/components/HoverPreview/HoverPreviewMention.tsx b/app/components/HoverPreview/HoverPreviewMention.tsx index 10ddde0c3..a79b209ab 100644 --- a/app/components/HoverPreview/HoverPreviewMention.tsx +++ b/app/components/HoverPreview/HoverPreviewMention.tsx @@ -15,10 +15,13 @@ type Props = { color: string; }; -function HoverPreviewMention({ url, title, info, color }: Props) { +const HoverPreviewMention = React.forwardRef(function _HoverPreviewMention( + { url, title, info, color }: Props, + ref: React.Ref +) { return ( - + ); -} +}); export default HoverPreviewMention;