mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
Show tooltip immediately if a tooltip is already open (#28870)
* Memoize delayProps * Refactor Escape key handler * Use useTooltipContext * Remove delay: 0 from tooltips * Only use isTooltipOpen if available * Only show transition for initial tooltip GitOrigin-RevId: 74950ea7e705acb8f42dea552b23ce93c66058c7
This commit is contained in:
@@ -74,7 +74,6 @@ export default function DictionaryModalContent({
|
||||
<OLTooltip
|
||||
id={`tooltip-remove-learned-word-${learnedWord}`}
|
||||
description={t('edit_dictionary_remove')}
|
||||
overlayProps={{ delay: 0 }}
|
||||
>
|
||||
<OLIconButton
|
||||
variant="danger"
|
||||
|
||||
@@ -30,6 +30,7 @@ import { UserFeaturesProvider } from '@/shared/context/user-features-context'
|
||||
import { UserSettingsProvider } from '@/shared/context/user-settings-context'
|
||||
import { IdeRedesignSwitcherProvider } from './ide-redesign-switcher-context'
|
||||
import { CommandRegistryProvider } from './command-registry-context'
|
||||
import { TooltipProvider } from '@/shared/context/tooltip-provider'
|
||||
|
||||
export const ReactContextRoot: FC<
|
||||
React.PropsWithChildren<{
|
||||
@@ -68,6 +69,7 @@ export const ReactContextRoot: FC<
|
||||
IdeRedesignSwitcherProvider,
|
||||
CommandRegistryProvider,
|
||||
UserFeaturesProvider,
|
||||
TooltipProvider,
|
||||
...providers,
|
||||
}
|
||||
|
||||
@@ -103,7 +105,9 @@ export const ReactContextRoot: FC<
|
||||
<Providers.OutlineProvider>
|
||||
<Providers.IdeRedesignSwitcherProvider>
|
||||
<Providers.CommandRegistryProvider>
|
||||
{children}
|
||||
<Providers.TooltipProvider>
|
||||
{children}
|
||||
</Providers.TooltipProvider>
|
||||
</Providers.CommandRegistryProvider>
|
||||
</Providers.IdeRedesignSwitcherProvider>
|
||||
</Providers.OutlineProvider>
|
||||
|
||||
@@ -78,7 +78,6 @@ const OnlineUserWidget = ({
|
||||
overlayProps={{
|
||||
placement: 'bottom',
|
||||
trigger: ['hover', 'focus'],
|
||||
delay: 0,
|
||||
}}
|
||||
>
|
||||
<button className="online-users-row-button" onClick={onClick}>
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function RailActionElement({ action }: { action: RailAction }) {
|
||||
<OLTooltip
|
||||
id={`rail-dropdown-tooltip-${action.key}`}
|
||||
description={action.title}
|
||||
overlayProps={{ delay: 0, placement: 'right' }}
|
||||
overlayProps={{ placement: 'right' }}
|
||||
>
|
||||
<span>
|
||||
<DropdownToggle
|
||||
@@ -68,7 +68,7 @@ export default function RailActionElement({ action }: { action: RailAction }) {
|
||||
<OLTooltip
|
||||
id={`rail-tab-tooltip-${action.key}`}
|
||||
description={action.title}
|
||||
overlayProps={{ delay: 0, placement: 'right' }}
|
||||
overlayProps={{ placement: 'right' }}
|
||||
>
|
||||
<button
|
||||
onClick={onActionClick}
|
||||
|
||||
@@ -21,7 +21,7 @@ const RailTab = forwardRef<
|
||||
<OLTooltip
|
||||
id={`rail-tab-tooltip-${eventKey}`}
|
||||
description={title}
|
||||
overlayProps={{ delay: 0, placement: 'right' }}
|
||||
overlayProps={{ placement: 'right' }}
|
||||
>
|
||||
<NavLink
|
||||
ref={ref}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function ChangeLayoutButton() {
|
||||
<OLTooltip
|
||||
id="tooltip-open-layout-options"
|
||||
description={t('layout_options')}
|
||||
overlayProps={{ delay: 0, placement: 'bottom' }}
|
||||
overlayProps={{ placement: 'bottom' }}
|
||||
>
|
||||
<span>
|
||||
<DropdownToggle
|
||||
|
||||
@@ -15,7 +15,7 @@ export const ToolbarLogos = ({ cobranding }: ToolbarLogosProps) => {
|
||||
<OLTooltip
|
||||
id="tooltip-home-button"
|
||||
description={t('back_to_your_projects')}
|
||||
overlayProps={{ delay: 0, placement: 'bottom' }}
|
||||
overlayProps={{ placement: 'bottom' }}
|
||||
>
|
||||
<div className="ide-redesign-toolbar-home-button">
|
||||
<a href="/project" className="ide-redesign-toolbar-home-link">
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function ShowHistoryButton() {
|
||||
<OLTooltip
|
||||
id="tooltip-open-history"
|
||||
description={t('history')}
|
||||
overlayProps={{ delay: 0, placement: 'bottom' }}
|
||||
overlayProps={{ placement: 'bottom' }}
|
||||
>
|
||||
<OLIconButton
|
||||
icon="history"
|
||||
|
||||
@@ -40,7 +40,7 @@ export const ColumnSizeIndicator = ({
|
||||
width: formattedWidth,
|
||||
})
|
||||
}
|
||||
overlayProps={{ delay: 0, placement: 'bottom' }}
|
||||
overlayProps={{ placement: 'bottom' }}
|
||||
>
|
||||
<button
|
||||
className="btn table-generator-column-indicator-button"
|
||||
|
||||
@@ -186,7 +186,7 @@ const ColumnWidthModalBody = () => {
|
||||
<OLTooltip
|
||||
id="table-generator-unit-tooltip"
|
||||
description={unitHelp.tooltip}
|
||||
overlayProps={{ delay: 0, placement: 'top' }}
|
||||
overlayProps={{ placement: 'top' }}
|
||||
>
|
||||
<span>
|
||||
<MaterialIcon type="help" className="align-middle" />
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
useEffect,
|
||||
forwardRef,
|
||||
useState,
|
||||
useMemo,
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import {
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
TooltipProps as BSTooltipProps,
|
||||
} from 'react-bootstrap'
|
||||
import { callFnsInSequence } from '@/utils/functions'
|
||||
import { useTooltipContext } from '@/shared/context/tooltip-provider'
|
||||
|
||||
const DEFAULT_DELAY_SHOW = 300
|
||||
// Slightly lower value avoids flickering when an adjacent tooltip is shown before the previous one hides
|
||||
@@ -34,6 +36,26 @@ const UpdatingTooltip = forwardRef<HTMLDivElement, BSTooltipProps>(
|
||||
)
|
||||
UpdatingTooltip.displayName = 'UpdatingTooltip'
|
||||
|
||||
const chooseDelayOptions = (
|
||||
delay?: number | { show: number; hide: number }
|
||||
): { show: number; hide: number } => {
|
||||
if (typeof delay === 'object') {
|
||||
return delay
|
||||
}
|
||||
|
||||
if (typeof delay === 'number') {
|
||||
return {
|
||||
show: delay,
|
||||
hide: Math.max(delay - 10, 0),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
show: DEFAULT_DELAY_SHOW,
|
||||
hide: DEFAULT_DELAY_HIDE,
|
||||
}
|
||||
}
|
||||
|
||||
export type TooltipProps = {
|
||||
description: React.ReactNode
|
||||
id: string
|
||||
@@ -53,20 +75,22 @@ function Tooltip({
|
||||
}: TooltipProps) {
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
const tooltipContext = useTooltipContext()
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (e: KeyboardEvent) => {
|
||||
if (show && e.key === 'Escape') {
|
||||
setShow(false)
|
||||
e.stopPropagation()
|
||||
}
|
||||
},
|
||||
[show, setShow]
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleKeyDown, true)
|
||||
return () => document.removeEventListener('keydown', handleKeyDown, true)
|
||||
}, [handleKeyDown])
|
||||
document.addEventListener('keydown', listener, true)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', listener, true)
|
||||
}
|
||||
}, [show])
|
||||
|
||||
const hideTooltip = (e: React.MouseEvent) => {
|
||||
if (e.currentTarget instanceof HTMLElement) {
|
||||
@@ -75,13 +99,22 @@ function Tooltip({
|
||||
setShow(false)
|
||||
}
|
||||
|
||||
const delay = overlayProps?.delay
|
||||
let delayShow = DEFAULT_DELAY_SHOW
|
||||
let delayHide = DEFAULT_DELAY_HIDE
|
||||
if (delay !== undefined) {
|
||||
delayShow = typeof delay === 'number' ? delay : delay.show
|
||||
delayHide = typeof delay === 'number' ? Math.max(delay - 10, 0) : delay.hide
|
||||
}
|
||||
const delayProps = useMemo(() => {
|
||||
const delayOptions = chooseDelayOptions(overlayProps?.delay)
|
||||
if (tooltipContext?.isTooltipOpen) {
|
||||
delayOptions.show = 0
|
||||
delayOptions.hide = 0
|
||||
}
|
||||
return delayOptions
|
||||
}, [overlayProps?.delay, tooltipContext])
|
||||
|
||||
const handleToggle = useCallback(
|
||||
(value: boolean) => {
|
||||
tooltipContext?.setIsTooltipOpen(value)
|
||||
setShow(value)
|
||||
},
|
||||
[tooltipContext]
|
||||
)
|
||||
|
||||
return (
|
||||
<OverlayTrigger
|
||||
@@ -95,10 +128,11 @@ function Tooltip({
|
||||
</UpdatingTooltip>
|
||||
}
|
||||
{...overlayProps}
|
||||
delay={{ show: delayShow, hide: delayHide }}
|
||||
delay={delayProps}
|
||||
placement={overlayProps?.placement || 'top'}
|
||||
show={show}
|
||||
onToggle={setShow}
|
||||
onToggle={handleToggle}
|
||||
transition={!tooltipContext?.isTooltipOpen}
|
||||
>
|
||||
{overlayProps?.trigger === 'click'
|
||||
? children
|
||||
|
||||
41
services/web/frontend/js/shared/context/tooltip-provider.tsx
Normal file
41
services/web/frontend/js/shared/context/tooltip-provider.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
createContext,
|
||||
Dispatch,
|
||||
FC,
|
||||
PropsWithChildren,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import useDebounce from '@/shared/hooks/use-debounce'
|
||||
|
||||
const TooltipContext = createContext<
|
||||
| {
|
||||
isTooltipOpen: boolean
|
||||
setIsTooltipOpen: Dispatch<SetStateAction<boolean>>
|
||||
}
|
||||
| undefined
|
||||
>(undefined)
|
||||
|
||||
export const TooltipProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
const [isTooltipOpen, setIsTooltipOpen] = useState(false)
|
||||
|
||||
const debouncedIsTooltipOpen = useDebounce(isTooltipOpen, 100)
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
isTooltipOpen: debouncedIsTooltipOpen,
|
||||
setIsTooltipOpen,
|
||||
}),
|
||||
[debouncedIsTooltipOpen, setIsTooltipOpen]
|
||||
)
|
||||
|
||||
return (
|
||||
<TooltipContext.Provider value={value}>{children}</TooltipContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useTooltipContext = () => {
|
||||
return useContext(TooltipContext)
|
||||
}
|
||||
Reference in New Issue
Block a user