From 31f8a3fb441291d96d762acd86ad6dd70d693d1e Mon Sep 17 00:00:00 2001 From: Apoorv Mishra Date: Tue, 25 Jul 2023 19:31:27 +0530 Subject: [PATCH] fix: hover card styling --- app/components/HoverPreview/Components.tsx | 69 ++++++++++- app/components/HoverPreview/HoverPreview.tsx | 117 ++++-------------- .../HoverPreview/HoverPreviewDocument.tsx | 41 +++--- .../HoverPreview/HoverPreviewLink.tsx | 35 ++++-- .../HoverPreview/HoverPreviewMention.tsx | 34 ++--- 5 files changed, 164 insertions(+), 132 deletions(-) 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} + + + + ); }