mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-20 03:53:45 +00:00
Merge pull request #21832 from overleaf/jel-search-badges
[web] Add tooltips when needed on badge links in gallery search results GitOrigin-RevId: edcdcf8134698f17f607e003627a8b4123519b0b
This commit is contained in:
parent
ade1b7f2bc
commit
5c63188200
5 changed files with 116 additions and 9 deletions
|
@ -17,9 +17,7 @@ const footerLanguageElement = document.querySelector(
|
|||
|
||||
const allTooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
|
||||
const possibleBadgeTooltips = document.querySelectorAll(
|
||||
'[data-bs-toggle="tooltip-if-needed"]'
|
||||
)
|
||||
const possibleBadgeTooltips = document.querySelectorAll('[data-badge-tooltip]')
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const footLangTooltip = new Tooltip(footerLanguageElement)
|
||||
|
@ -30,7 +28,7 @@ allTooltips.forEach(element => {
|
|||
})
|
||||
|
||||
possibleBadgeTooltips.forEach(element => {
|
||||
// Put data-bs-toggle="tooltip-if-needed" on .badge-content
|
||||
// Put data-badge-tooltip on .badge-content
|
||||
// then tooltip is only shown if content is clipped due to max-width on .badge
|
||||
// Due to font loading, the width calculated on page load might change, so we might
|
||||
// incorrectly determine a tooltip is not needed. This is why max-width will always be set to none
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import { OverlayTrigger, Tooltip } from 'react-bootstrap-5'
|
||||
import type { MergeAndOverride } from '../../../../../../types/utils'
|
||||
import BadgeLink, { type BadgeLinkProps } from './badge-link'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import classNames from 'classnames'
|
||||
|
||||
type BadgeLinkWithTooltipProps = MergeAndOverride<
|
||||
BadgeLinkProps,
|
||||
{
|
||||
placement?: 'top' | 'right' | 'bottom' | 'left'
|
||||
tooltipTitle: string
|
||||
}
|
||||
>
|
||||
|
||||
function getElementWidth(el: Element) {
|
||||
const elComputedStyle = window.getComputedStyle(el)
|
||||
const elPaddingX =
|
||||
parseFloat(elComputedStyle.paddingLeft) +
|
||||
parseFloat(elComputedStyle.paddingRight)
|
||||
const elBorderX =
|
||||
parseFloat(elComputedStyle.borderLeftWidth) +
|
||||
parseFloat(elComputedStyle.borderRightWidth)
|
||||
return el.scrollWidth - elPaddingX - elBorderX
|
||||
}
|
||||
|
||||
function BadgeLinkWithTooltip({
|
||||
children,
|
||||
tooltipTitle,
|
||||
placement,
|
||||
...rest
|
||||
}: BadgeLinkWithTooltipProps) {
|
||||
const badgeContentRef = useRef<HTMLElement>(null)
|
||||
const [showTooltip, setShowTooltip] = useState(true)
|
||||
const [noMaxWidth, setNoMaxWidth] = useState(false)
|
||||
|
||||
const badgeLinkClasses = classNames({ 'badge-link-no-max-width': noMaxWidth })
|
||||
|
||||
const renderTooltip = (props: any) => {
|
||||
if (showTooltip) {
|
||||
return (
|
||||
<Tooltip
|
||||
id={`badge-tooltip-${rest.href.replace(/\//g, '-')}`}
|
||||
{...props}
|
||||
>
|
||||
{tooltipTitle}
|
||||
</Tooltip>
|
||||
)
|
||||
} else {
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (badgeContentRef.current) {
|
||||
// Check if tooltip needed.
|
||||
// If .badge-content does not extend beyond max-width limit on
|
||||
// .badge then tooltip is not needed. max-width is always
|
||||
// removed when withTooltip exists and tooltip is not needed
|
||||
// to avoid any differences in width calculation after font
|
||||
// loaded (for example, Noto sans). Othwerise, badge might get
|
||||
// clipped due to font loaded causing .badge-content to be
|
||||
// greater than .badge max-width and no tooltip was determined
|
||||
// to be needed with default font (for example, sans-serif)
|
||||
const badgeContentWidth = badgeContentRef.current.scrollWidth
|
||||
if (badgeContentRef.current?.parentElement) {
|
||||
const badgeWidth = getElementWidth(
|
||||
badgeContentRef.current?.parentElement
|
||||
)
|
||||
if (badgeContentWidth <= badgeWidth) {
|
||||
// no tooltip and remove max-width
|
||||
setNoMaxWidth(true)
|
||||
setShowTooltip(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<OverlayTrigger placement={placement || 'bottom'} overlay={renderTooltip}>
|
||||
<span>
|
||||
<BadgeLink
|
||||
{...rest}
|
||||
badgeContentRef={badgeContentRef}
|
||||
badgeLinkClasses={badgeLinkClasses}
|
||||
>
|
||||
{children}
|
||||
</BadgeLink>
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
export default BadgeLinkWithTooltip
|
|
@ -2,15 +2,22 @@ import classNames from 'classnames'
|
|||
import type { MergeAndOverride } from '../../../../../../types/utils'
|
||||
import Badge, { type BadgeProps } from './badge'
|
||||
|
||||
type BadgeLinkProps = MergeAndOverride<
|
||||
export type BadgeLinkProps = MergeAndOverride<
|
||||
BadgeProps,
|
||||
{
|
||||
href: string
|
||||
badgeContentRef?: React.RefObject<HTMLElement>
|
||||
badgeLinkClasses?: string
|
||||
}
|
||||
>
|
||||
|
||||
function BadgeLink({ href, children, ...badgeProps }: BadgeLinkProps) {
|
||||
const containerClass = classNames('badge-link', {
|
||||
function BadgeLink({
|
||||
href,
|
||||
badgeLinkClasses,
|
||||
children,
|
||||
...badgeProps
|
||||
}: BadgeLinkProps) {
|
||||
const containerClass = classNames(badgeLinkClasses, 'badge-link', {
|
||||
[`badge-link-${badgeProps.bg}`]: badgeProps.bg,
|
||||
})
|
||||
|
||||
|
|
|
@ -5,14 +5,17 @@ export type BadgeProps = MergeAndOverride<
|
|||
BSBadgeProps,
|
||||
{
|
||||
prepend?: React.ReactNode
|
||||
badgeContentRef?: React.RefObject<HTMLElement>
|
||||
}
|
||||
>
|
||||
|
||||
function Badge({ prepend, children, ...rest }: BadgeProps) {
|
||||
function Badge({ prepend, children, badgeContentRef, ...rest }: BadgeProps) {
|
||||
return (
|
||||
<BSBadge {...rest}>
|
||||
{prepend && <span className="badge-prepend">{prepend}</span>}
|
||||
<span className="badge-content">{children}</span>
|
||||
<span className="badge-content" ref={badgeContentRef}>
|
||||
{children}
|
||||
</span>
|
||||
</BSBadge>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -105,6 +105,12 @@ $max-width: 160px;
|
|||
background-color: var(--neutral-30) !important;
|
||||
}
|
||||
|
||||
&.badge-link-no-max-width {
|
||||
.badge {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
@include body-sm;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue