From c4fe93a8b8f013aff5b05893b33fc9e0ca6ca119 Mon Sep 17 00:00:00 2001 From: yyh Date: Mon, 2 Mar 2026 18:03:25 +0800 Subject: [PATCH] refactor(ui): compose tooltip primitives and dedupe menu popup --- .../base/ui/dropdown-menu/index.tsx | 72 +++++++++------ web/app/components/base/ui/tooltip/index.tsx | 88 +++++++++---------- .../header/account-dropdown/compliance.tsx | 29 ++++-- 3 files changed, 106 insertions(+), 83 deletions(-) diff --git a/web/app/components/base/ui/dropdown-menu/index.tsx b/web/app/components/base/ui/dropdown-menu/index.tsx index 1f12be0464..880e98790a 100644 --- a/web/app/components/base/ui/dropdown-menu/index.tsx +++ b/web/app/components/base/ui/dropdown-menu/index.tsx @@ -7,6 +7,7 @@ import { parsePlacement } from '@/app/components/base/ui/placement' import { cn } from '@/utils/classnames' export const DropdownMenu = Menu.Root +export const DropdownMenuPortal = Menu.Portal export const DropdownMenuTrigger = Menu.Trigger export const DropdownMenuSub = Menu.SubmenuRoot export const DropdownMenuGroup = Menu.Group @@ -26,14 +27,22 @@ type DropdownMenuContentProps = { popupClassName?: string } -export function DropdownMenuContent({ +type DropdownMenuPopupProps = Required> & { + placement: Placement + sideOffset: number + alignOffset: number + className?: string + popupClassName?: string +} + +function DropdownMenuPopup({ children, - placement = 'bottom-end', - sideOffset = 4, - alignOffset = 0, + placement, + sideOffset, + alignOffset, className, popupClassName, -}: DropdownMenuContentProps) { +}: DropdownMenuPopupProps) { const { side, align } = parsePlacement(placement) return ( @@ -43,7 +52,7 @@ export function DropdownMenuContent({ align={align} sideOffset={sideOffset} alignOffset={alignOffset} - className={cn('outline-none', className)} + className={cn('isolate outline-none', className)} > + {children} + + ) +} + type DropdownMenuSubTriggerProps = React.ComponentPropsWithoutRef & { destructive?: boolean } @@ -98,28 +128,16 @@ export function DropdownMenuSubContent({ className, popupClassName, }: DropdownMenuSubContentProps) { - const { side, align } = parsePlacement(placement) - return ( - - - - {children} - - - + + {children} + ) } diff --git a/web/app/components/base/ui/tooltip/index.tsx b/web/app/components/base/ui/tooltip/index.tsx index fb27803eb7..9fad2a77c5 100644 --- a/web/app/components/base/ui/tooltip/index.tsx +++ b/web/app/components/base/ui/tooltip/index.tsx @@ -6,61 +6,53 @@ import * as React from 'react' import { parsePlacement } from '@/app/components/base/ui/placement' import { cn } from '@/utils/classnames' -export type TooltipProps = { - position?: Placement - disabled?: boolean - popupContent?: React.ReactNode - children?: React.ReactNode +type TooltipContentVariant = 'default' | 'plain' + +export type TooltipContentProps = { + children: React.ReactNode + placement?: Placement + sideOffset?: number + alignOffset?: number + className?: string popupClassName?: string - noDecoration?: boolean - offset?: number - delay?: number - closeDelay?: number -} + variant?: TooltipContentVariant +} & Omit, 'children'> -const Tooltip = React.memo(({ - position = 'top', - disabled = false, - popupContent, +export function TooltipContent({ children, + placement = 'top', + sideOffset = 8, + alignOffset = 0, + className, popupClassName, - noDecoration, - offset = 8, - delay, - closeDelay, -}: TooltipProps) => { - const { side, align } = parsePlacement(position) - - if (!popupContent || disabled) - return <>{children} + variant = 'default', + ...props +}: TooltipContentProps) { + const { side, align } = parsePlacement(placement) return ( - - {React.isValidElement(children) - ? - : }>{children}} - - + + - - {popupContent} - - - - + {children} + + + ) -}) - -Tooltip.displayName = 'Tooltip' +} export const TooltipProvider = BaseTooltip.Provider -export default Tooltip +export const Tooltip = BaseTooltip.Root +export const TooltipTrigger = BaseTooltip.Trigger diff --git a/web/app/components/header/account-dropdown/compliance.tsx b/web/app/components/header/account-dropdown/compliance.tsx index 3841d9f0c4..6bf19674d0 100644 --- a/web/app/components/header/account-dropdown/compliance.tsx +++ b/web/app/components/header/account-dropdown/compliance.tsx @@ -3,7 +3,7 @@ import { useMutation } from '@tanstack/react-query' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '@/app/components/base/ui/dropdown-menu' -import Tooltip from '@/app/components/base/ui/tooltip' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { Plan } from '@/app/components/billing/type' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { useModalContext } from '@/context/modal-context' @@ -83,14 +83,27 @@ function ComplianceDocActionVisual({ ) } + const canShowUpgradeTooltip = tooltipText.length > 0 + return ( - - - -
- {upgradeText} -
-
+ + + +
+ {upgradeText} +
+ + )} + /> + {canShowUpgradeTooltip && ( + + {tooltipText} + + )}
) }