diff --git a/app/components/HoverPreview/Components.tsx b/app/components/HoverPreview/Components.tsx
index 4856c64a1..4c59de088 100644
--- a/app/components/HoverPreview/Components.tsx
+++ b/app/components/HoverPreview/Components.tsx
@@ -1,9 +1,16 @@
+import { transparentize } from "polished";
import * as React from "react";
import { Link } from "react-router-dom";
import styled 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;
+
const StyledText = styled(Text)`
margin-bottom: 0;
padding-top: 0.125em;
@@ -11,7 +18,11 @@ const StyledText = styled(Text)`
export const Preview = styled(Link)`
cursor: var(--pointer);
- margin-bottom: 0;
+ border-radius: 4px;
+ box-shadow: 0 30px 90px -20px rgba(0, 0, 0, 0.3),
+ 0 0 1px 1px rgba(0, 0, 0, 0.05);
+ overflow: hidden;
+ position: absolute;
${(props) => (!props.to ? "pointer-events: none;" : "")}
`;
@@ -21,7 +32,7 @@ export const Title = styled.h2`
color: ${s("text")};
`;
-export const Info: React.FC = ({ children }) => (
+export const Info: React.FC = ({ children }: { children: string }) => (
{children}
@@ -34,3 +45,57 @@ export const Description: React.FC = styled(StyledText)`
export const DescriptionContainer = styled.div`
margin-top: 0.5em;
`;
+
+export const CardContent = styled.div`
+ overflow: hidden;
+ max-height: 20.5em;
+ user-select: none;
+`;
+
+// &:after — gradient mask for overflow text
+export const Card = styled.div<{ fadeOut?: boolean; $borderRadius?: string }>`
+ backdrop-filter: blur(10px);
+ background: ${(props) => props.theme.menuBackground};
+ padding: ${CARD_PADDING}px;
+ width: ${CARD_WIDTH}px;
+ font-size: 0.9em;
+ position: relative;
+
+ .placeholder,
+ .heading-anchor {
+ display: none;
+ }
+
+ // fills the gap between the card and pointer to avoid a dead zone
+ &::before {
+ content: "";
+ position: absolute;
+ top: -10px;
+ left: 0;
+ right: 0;
+ height: 10px;
+ }
+
+ ${(props) =>
+ props.fadeOut !== false
+ ? `&:after {
+ content: "";
+ display: block;
+ position: absolute;
+ pointer-events: none;
+ background: linear-gradient(
+ 90deg,
+ ${transparentize(1, props.theme.menuBackground)} 0%,
+ ${transparentize(1, props.theme.menuBackground)} 75%,
+ ${props.theme.menuBackground} 90%
+ );
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 1.7em;
+ border-bottom: 16px solid ${props.theme.menuBackground};
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ }`
+ : ""}
+`;
diff --git a/app/components/HoverPreview/HoverPreview.tsx b/app/components/HoverPreview/HoverPreview.tsx
index da68fed4c..d34bedd85 100644
--- a/app/components/HoverPreview/HoverPreview.tsx
+++ b/app/components/HoverPreview/HoverPreview.tsx
@@ -1,5 +1,4 @@
import { m } from "framer-motion";
-import { transparentize } from "polished";
import * as React from "react";
import { Portal } from "react-portal";
import styled from "styled-components";
@@ -12,14 +11,13 @@ 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 HoverPreviewDocument from "./HoverPreviewDocument";
import HoverPreviewLink from "./HoverPreviewLink";
import HoverPreviewMention from "./HoverPreviewMention";
const DELAY_OPEN = 300;
const DELAY_CLOSE = 600;
-const CARD_PADDING = 16;
-const CARD_MAX_WIDTH = 375;
type Props = {
/* The HTML element that is being hovered over */
@@ -123,10 +121,7 @@ function HoverPreviewInternal({ element, onClose }: Props) {
const elemBounds = element.getBoundingClientRect();
const cardBounds = cardRef.current?.getBoundingClientRect();
const left = cardBounds
- ? Math.min(
- elemBounds.left,
- window.innerWidth - CARD_PADDING - CARD_MAX_WIDTH
- )
+ ? Math.min(elemBounds.left, window.innerWidth - CARD_PADDING - CARD_WIDTH)
: elemBounds.left;
const leftOffset = elemBounds.left - left;
@@ -151,33 +146,29 @@ function HoverPreviewInternal({ element, onClose }: Props) {
initial={{ opacity: 0, y: -20, pointerEvents: "none" }}
animate={{ opacity: 1, y: 0, pointerEvents: "auto" }}
>
-
-
- {data.type === UnfurlType.Mention ? (
-
- ) : data.type === UnfurlType.Document ? (
-
- ) : (
-
- )}
-
-
+ {data.type === UnfurlType.Mention ? (
+
+ ) : data.type === UnfurlType.Document ? (
+
+ ) : (
+
+ )}
) : null}
@@ -202,64 +193,6 @@ const Animate = styled(m.div)`
}
`;
-const CardContent = styled.div`
- overflow: hidden;
- max-height: 20.5em;
- user-select: none;
-`;
-
-// &:after — gradient mask for overflow text
-const Card = styled.div<{ fadeOut?: boolean }>`
- backdrop-filter: blur(10px);
- background: ${(props) => props.theme.menuBackground};
- border-radius: 4px;
- box-shadow: 0 30px 90px -20px rgba(0, 0, 0, 0.3),
- 0 0 1px 1px rgba(0, 0, 0, 0.05);
- padding: ${CARD_PADDING}px;
- min-width: 350px;
- max-width: ${CARD_MAX_WIDTH}px;
- font-size: 0.9em;
- position: relative;
-
- .placeholder,
- .heading-anchor {
- display: none;
- }
-
- // fills the gap between the card and pointer to avoid a dead zone
- &::before {
- content: "";
- position: absolute;
- top: -10px;
- left: 0;
- right: 0;
- height: 10px;
- }
-
- ${(props) =>
- props.fadeOut !== false
- ? `&:after {
- content: "";
- display: block;
- position: absolute;
- pointer-events: none;
- background: linear-gradient(
- 90deg,
- ${transparentize(1, props.theme.menuBackground)} 0%,
- ${transparentize(1, props.theme.menuBackground)} 75%,
- ${props.theme.menuBackground} 90%
- );
- bottom: 0;
- left: 0;
- right: 0;
- height: 1.7em;
- border-bottom: 16px solid ${props.theme.menuBackground};
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 4px;
- }`
- : ""}
-`;
-
const Position = styled.div<{ fixed?: boolean; top?: number; left?: number }>`
margin-top: 10px;
position: ${({ fixed }) => (fixed ? "fixed" : "absolute")};
diff --git a/app/components/HoverPreview/HoverPreviewDocument.tsx b/app/components/HoverPreview/HoverPreviewDocument.tsx
index 7e171ace9..7d21c7b03 100644
--- a/app/components/HoverPreview/HoverPreviewDocument.tsx
+++ b/app/components/HoverPreview/HoverPreviewDocument.tsx
@@ -1,7 +1,14 @@
import * as React from "react";
import Editor from "~/components/Editor";
import Flex from "~/components/Flex";
-import { Preview, Title, Info, DescriptionContainer } from "./Components";
+import {
+ Preview,
+ Title,
+ Info,
+ DescriptionContainer,
+ Card,
+ CardContent,
+} from "./Components";
type Props = {
/** Document id associated with the editor, if any */
@@ -19,20 +26,24 @@ type Props = {
function HoverPreviewDocument({ id, url, title, info, description }: Props) {
return (
-
- {title}
- {info}
-
- }>
-
-
-
-
+
+
+
+ {title}
+ {info}
+
+ }>
+
+
+
+
+
+
);
}
diff --git a/app/components/HoverPreview/HoverPreviewLink.tsx b/app/components/HoverPreview/HoverPreviewLink.tsx
index d8db1fe42..259724900 100644
--- a/app/components/HoverPreview/HoverPreviewLink.tsx
+++ b/app/components/HoverPreview/HoverPreviewLink.tsx
@@ -1,7 +1,16 @@
import * as React from "react";
+import styled from "styled-components";
import Img from "@shared/editor/components/Img";
import Flex from "~/components/Flex";
-import { Preview, Title, Description } from "./Components";
+import {
+ Preview,
+ Title,
+ Description,
+ Card,
+ CardContent,
+ CARD_WIDTH,
+ THUMBNAIL_HEIGHT,
+} from "./Components";
type Props = {
/** Link url */
@@ -16,16 +25,26 @@ type Props = {
function HoverPreviewLink({ url, thumbnailUrl, title, description }: Props) {
return (
-
-
-
-
- {title}
- {description}
-
+
+
+ {thumbnailUrl ? : null}
+
+
+
+ {title}
+ {description}
+
+
+
);
}
+const Thumbnail = styled(Img)`
+ object-fit: cover;
+ max-width: ${CARD_WIDTH}px;
+ height: ${THUMBNAIL_HEIGHT}px;
+`;
+
export default HoverPreviewLink;
diff --git a/app/components/HoverPreview/HoverPreviewMention.tsx b/app/components/HoverPreview/HoverPreviewMention.tsx
index f30d26f6e..63d61dbef 100644
--- a/app/components/HoverPreview/HoverPreviewMention.tsx
+++ b/app/components/HoverPreview/HoverPreviewMention.tsx
@@ -2,7 +2,7 @@ import * as React from "react";
import Avatar from "~/components/Avatar";
import { AvatarSize } from "~/components/Avatar/Avatar";
import Flex from "~/components/Flex";
-import { Preview, Title, Info } from "./Components";
+import { Preview, Title, Info, Card, CardContent } from "./Components";
type Props = {
/** Resource url, avatar url in case of user mention */
@@ -18,20 +18,24 @@ type Props = {
function HoverPreviewMention({ url, title, info, color }: Props) {
return (
-
-
-
- {title}
- {info}
-
-
+
+
+
+
+
+ {title}
+ {info}
+
+
+
+
);
}