From ae871fc506ff1a682ee6983668a911f6038ff9ef Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Thu, 10 Oct 2024 15:56:11 +0100 Subject: [PATCH] 14.6.8 - pricing card title tooltips, support for multiple tooltip orientations --- package.json | 2 +- src/core/Pricing/PricingCards.tsx | 23 ++++++++--- src/core/Pricing/data.tsx | 19 +++++++++ src/core/Pricing/types.ts | 1 + src/core/Tooltip.tsx | 66 ++++++++++++++++++++++--------- 5 files changed, 86 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 76b7bc97..34643177 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ably/ui", - "version": "14.6.7", + "version": "14.6.8", "description": "Home of the Ably design system library ([design.ably.com](https://design.ably.com)). It provides a showcase, development/test environment and a publishing pipeline for different distributables.", "repository": { "type": "git", diff --git a/src/core/Pricing/PricingCards.tsx b/src/core/Pricing/PricingCards.tsx index 0a9b6fe2..2c3768f1 100644 --- a/src/core/Pricing/PricingCards.tsx +++ b/src/core/Pricing/PricingCards.tsx @@ -1,11 +1,12 @@ import React, { Fragment, useEffect, useRef, useState } from "react"; +import throttle from "lodash.throttle"; import type { PricingDataFeature } from "./types"; import { determineThemeColor } from "../styles/colors/utils"; import { ColorClass, Theme } from "../styles/colors/types"; import Icon from "../Icon"; import FeaturedLink from "../FeaturedLink"; import { IconName } from "../Icon/types"; -import throttle from "lodash.throttle"; +import Tooltip from "../Tooltip"; export type PricingCardsProps = { data: PricingDataFeature[]; @@ -105,11 +106,21 @@ const PricingCards = ({ className={`relative z-10 flex flex-col gap-24 ${delimiter ? "@[520px]:flex-1 @[920px]:flex-none" : ""}`} >
-

- {title.content} -

+
+

+ {title.content} +

+ {title.tooltip ? ( + + {title.tooltip} + + ) : null} +

+ Ably aggregates all its data into named units of distribution, + referred to as “channels”. These are used to transmit from one device + to another.{" "} + + Find out more. + +

+ ), }, description: { content: @@ -255,6 +265,15 @@ export const consumptionData: PricingDataFeature[] = [ content: "Connections", className: "ui-text-h3", color: "text-neutral-000", + tooltip: ( +

+ Clients establish and maintain a connection to the Ably service using + the most efficient transport available, typically WebSockets.{" "} + + Find out more. + +

+ ), }, description: { content: diff --git a/src/core/Pricing/types.ts b/src/core/Pricing/types.ts index f0f56907..028970fb 100644 --- a/src/core/Pricing/types.ts +++ b/src/core/Pricing/types.ts @@ -5,6 +5,7 @@ type PricingDataHeader = { content: string; className?: string; color?: ColorClass; + tooltip?: string | ReactNode; }; type PricingDataFeatureCta = { diff --git a/src/core/Tooltip.tsx b/src/core/Tooltip.tsx index 38d8abd8..652e9161 100644 --- a/src/core/Tooltip.tsx +++ b/src/core/Tooltip.tsx @@ -33,7 +33,7 @@ const Tooltip = ({ }: PropsWithChildren) => { const [open, setOpen] = useState(false); const [fadeOut, setFadeOut] = useState(false); - const [position, setPosition] = useState({ x: 0, y: 0 }); + const [position, setPosition] = useState({ x: 0, y: 0, orientation: "top" }); const offset = 8; const reference = useRef(null); const floating = useRef(null); @@ -47,6 +47,7 @@ const Tooltip = ({ const referenceRect = reference.current?.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; + let orientation = "top"; if (floatingRect && referenceRect) { let x = @@ -60,16 +61,19 @@ const Tooltip = ({ // Adjust if tooltip goes off the right edge if (x + floatingRect.width > viewportWidth + window.scrollX) { x = viewportWidth + window.scrollX - floatingRect.width - offset; + orientation = "left"; } // Adjust if tooltip goes off the left edge if (x < window.scrollX) { x = window.scrollX + offset; + orientation = "right"; } // Adjust if tooltip goes off the top edge if (y < window.scrollY) { y = referenceRect.bottom + offset + window.scrollY; + orientation = "bottom"; } // Adjust if tooltip goes off the bottom edge @@ -77,10 +81,10 @@ const Tooltip = ({ y = referenceRect.top - floatingRect.height - offset + window.scrollY; } - setPosition({ x, y }); + setPosition({ x, y, orientation }); } } else { - setPosition({ x: 0, y: 0 }); + setPosition({ x: 0, y: 0, orientation: "top" }); } return () => { @@ -98,30 +102,54 @@ const Tooltip = ({ }, 250); }; - const cursorHeadsNorth = ( + const cursorTowardsTooltip = ( event: MouseEvent, ref: RefObject, ) => { - if (ref.current) { - const { clientX, clientY } = event; - const { x, y, width } = ref.current.getBoundingClientRect(); - return clientX >= x && clientX <= x + width && clientY < y; + if (!ref.current) { + return false; } - return false; + const { clientX, clientY } = event; + const { x, y, width, height } = ref.current.getBoundingClientRect(); + const { orientation } = position; + + switch (orientation) { + case "top": + return clientX >= x && clientX <= x + width && clientY < y; + case "left": + return clientY >= y && clientY <= y + height && clientX < x; + case "right": + return clientY >= y && clientY <= y + height && clientX > x + width; + case "bottom": + return clientX >= x && clientX <= x + width && clientY > y + height; + default: + return false; + } + }; + + const fadeOutIfNotWithinTrigger = (event: MouseEvent) => { + if (!reference.current) return; + + const { clientX, clientY } = event; + const { x, y, width, height } = reference.current.getBoundingClientRect(); + const withinBounds = + clientX >= x && + clientX <= x + width && + clientY >= y && + clientY <= y + height; + + if (!withinBounds) { + initiateFadeOut(); + } }; return ( -
+