refactor(web): migrate legacy tooltip callers

This commit is contained in:
yyh 2026-05-09 12:24:50 +08:00
parent 140ad6ba4e
commit 02e51e3b4a
No known key found for this signature in database
47 changed files with 750 additions and 608 deletions

View File

@ -1,14 +1,14 @@
'use client'
import type { AccessControlAccount, AccessControlGroup } from '@/models/access-control'
import { Avatar } from '@langgenius/dify-ui/avatar'
import { RiAlertFill, RiCloseCircleFill, RiLockLine, RiOrganizationChart } from '@remixicon/react'
import { RiCloseCircleFill, RiLockLine, RiOrganizationChart } from '@remixicon/react'
import { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { AccessMode } from '@/models/access-control'
import { useAppWhiteListSubjects } from '@/service/access-control'
import useAccessControlStore from '../../../../context/access-control-store'
import { Infotip } from '../../base/infotip'
import Loading from '../../base/loading'
import Tooltip from '../../base/tooltip'
import AddMemberOrGroupDialog from './add-member-or-group-pop'
export default function SpecificGroupsOrMembers() {
@ -137,9 +137,14 @@ function BaseItem({ icon, onRemove, children }: BaseItemProps) {
export function WebAppSSONotEnabledTip() {
const { t } = useTranslation()
const tip = t('accessControlDialog.webAppSSONotEnabledTip', { ns: 'app' })
return (
<Tooltip asChild={false} popupContent={t('accessControlDialog.webAppSSONotEnabledTip', { ns: 'app' })}>
<RiAlertFill className="h-4 w-4 shrink-0 text-text-warning-secondary" />
</Tooltip>
<Infotip
aria-label={tip}
iconClassName="h-4 w-4 shrink-0 text-text-warning-secondary hover:text-text-warning-secondary"
>
{tip}
</Infotip>
)
}

View File

@ -22,7 +22,7 @@ import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ReactSortable } from 'react-sortablejs'
import { useContext } from 'use-context-selector'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import { InputVarType } from '@/app/components/workflow/types'
import ConfigContext from '@/context/debug-configuration'
import { useEventEmitterContextContext } from '@/context/event-emitter'
@ -257,13 +257,9 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
<div className="flex items-center">
<div className="mr-1">{t('variableTitle', { ns: 'appDebug' })}</div>
{!readonly && (
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('variableTip', { ns: 'appDebug' })}
</div>
)}
/>
<Infotip aria-label={t('variableTip', { ns: 'appDebug' })} popupClassName="w-[180px]">
{t('variableTip', { ns: 'appDebug' })}
</Infotip>
)}
</div>
)}

View File

@ -2,7 +2,7 @@
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import * as React from 'react'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
type Props = {
className?: string
@ -24,14 +24,9 @@ const ItemPanel: FC<Props> = ({
<div className="flex items-center">
{icon}
<div className="mr-1 ml-3 text-sm leading-6 font-semibold text-text-secondary">{name}</div>
<Tooltip
popupContent={(
<div className="w-[180px]">
{description}
</div>
)}
>
</Tooltip>
<Infotip aria-label={description} popupClassName="w-[180px]">
{description}
</Infotip>
</div>
<div>
{children}

View File

@ -1,9 +1,9 @@
'use client'
import type { FC } from 'react'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiCloseLine, RiPlayLargeLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { useStore } from '@/app/components/app/store'
import TooltipPlus from '@/app/components/base/tooltip'
import { WorkflowContextProvider } from '@/app/components/workflow/context'
import Run from '@/app/components/workflow/run'
import { useRouter } from '@/next/navigation'
@ -33,19 +33,23 @@ const DetailPanel: FC<ILogDetail> = ({ runID, onClose, canReplay = false }) => {
<div className="flex items-center bg-components-panel-bg">
<h1 className="shrink-0 px-4 py-1 system-xl-semibold text-text-primary">{t('runDetail.workflowTitle', { ns: 'appLog' })}</h1>
{canReplay && (
<TooltipPlus
popupContent={t('runDetail.testWithParams', { ns: 'appLog' })}
popupClassName="rounded-xl"
>
<button
type="button"
className="mr-1 flex h-6 w-6 items-center justify-center rounded-md hover:bg-state-base-hover"
aria-label={t('runDetail.testWithParams', { ns: 'appLog' })}
onClick={handleReplay}
>
<RiPlayLargeLine className="h-4 w-4 text-text-tertiary" />
</button>
</TooltipPlus>
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
className="mr-1 flex h-6 w-6 items-center justify-center rounded-md hover:bg-state-base-hover"
aria-label={t('runDetail.testWithParams', { ns: 'appLog' })}
onClick={handleReplay}
>
<RiPlayLargeLine className="h-4 w-4 text-text-tertiary" />
</button>
)}
/>
<TooltipContent className="rounded-xl">
{t('runDetail.testWithParams', { ns: 'appLog' })}
</TooltipContent>
</Tooltip>
)}
</div>
<WorkflowContextProvider>

View File

@ -23,14 +23,10 @@ const ProgressTooltip: FC<ProgressTooltipProps> = ({
onOpenChange={setOpen}
>
<TooltipTrigger
render={(
<div
data-testid="progress-trigger-content"
className="flex grow items-center"
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
/>
)}
data-testid="progress-trigger-content"
className="flex grow items-center border-0 bg-transparent p-0 text-left"
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
>
<div className="mr-1 h-1.5 w-16 overflow-hidden rounded-[3px] border border-components-progress-gray-border">
<div
@ -45,7 +41,6 @@ const ProgressTooltip: FC<ProgressTooltipProps> = ({
<TooltipContent
data-testid="progress-tooltip-popup"
placement="top-start"
sideOffset={0}
className="rounded-lg bg-components-tooltip-bg p-3 system-xs-medium text-text-quaternary shadow-lg"
>
{t('chat.citation.hitScore', { ns: 'common' })}

View File

@ -26,14 +26,10 @@ const Tooltip: FC<TooltipProps> = ({
onOpenChange={setOpen}
>
<TooltipTrigger
render={(
<div
data-testid="tooltip-trigger-content"
className="mr-6 flex items-center"
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
/>
)}
data-testid="tooltip-trigger-content"
className="mr-6 flex items-center border-0 bg-transparent p-0 text-left"
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
>
{icon}
{data}
@ -41,7 +37,6 @@ const Tooltip: FC<TooltipProps> = ({
<TooltipContent
data-testid="tooltip-popup"
placement="top-start"
sideOffset={0}
className="rounded-lg bg-components-tooltip-bg p-3 system-xs-medium text-text-quaternary shadow-lg"
>
{text}

View File

@ -1,8 +1,8 @@
'use client'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useClipboard } from '@/hooks/use-clipboard'
import Tooltip from '../tooltip'
type Props = {
content: string
@ -25,14 +25,25 @@ const CopyIcon = ({ content }: Props) => {
const safeTooltipText = tooltipText || ''
return (
<Tooltip
popupContent={safeTooltipText}
>
<div onMouseLeave={reset}>
{!copied
? (<span className="mx-1 i-custom-vender-line-files-copy h-3.5 w-3.5 cursor-pointer text-text-tertiary" onClick={handleCopy} data-testid="copy-icon" />)
: (<span className="mx-1 i-custom-vender-line-files-copy-check h-3.5 w-3.5 text-text-tertiary" data-testid="copied-icon" />)}
</div>
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label={safeTooltipText}
className="mx-1 inline-flex h-3.5 w-3.5 cursor-pointer border-0 bg-transparent p-0 text-text-tertiary"
onClick={handleCopy}
onMouseLeave={reset}
>
{!copied
? (<span aria-hidden className="i-custom-vender-line-files-copy h-3.5 w-3.5" data-testid="copy-icon" />)
: (<span aria-hidden className="i-custom-vender-line-files-copy-check h-3.5 w-3.5" data-testid="copied-icon" />)}
</button>
)}
/>
<TooltipContent>
{safeTooltipText}
</TooltipContent>
</Tooltip>
)
}

View File

@ -1,7 +1,7 @@
'use client'
import type { FC } from 'react'
import * as React from 'react'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
export const Item: FC<{ title: string, tooltip: string, children: React.JSX.Element }> = ({
title,
@ -12,11 +12,9 @@ export const Item: FC<{ title: string, tooltip: string, children: React.JSX.Elem
<div>
<div className="mb-1 flex items-center space-x-1">
<div className="py-1 system-sm-semibold text-text-secondary">{title}</div>
<Tooltip
popupContent={
<div className="max-w-[200px] system-sm-regular text-text-secondary">{tooltip}</div>
}
/>
<Infotip aria-label={tooltip} popupClassName="max-w-[200px] system-sm-regular text-text-secondary">
{tooltip}
</Infotip>
</div>
<div>{children}</div>
</div>

View File

@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next'
import { replace } from 'string-ts'
import AudioBtn from '@/app/components/base/audio-btn'
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import { languages } from '@/i18n-config/language'
import { usePathname } from '@/next/navigation'
import { useAppVoices } from '@/service/use-apps'
@ -89,17 +89,16 @@ const VoiceParamConfig = ({
<div className="mb-3">
<div className="mb-1 flex items-center py-1 system-sm-semibold text-text-secondary">
{t('voice.voiceSettings.language', { ns: 'appDebug' })}
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('voice.voiceSettings.resolutionTooltip', { ns: 'appDebug' }).split('\n').map(item => (
<div key={item}>
{item}
</div>
))}
<Infotip
aria-label={t('voice.voiceSettings.resolutionTooltip', { ns: 'appDebug' })}
popupClassName="w-[180px]"
>
{t('voice.voiceSettings.resolutionTooltip', { ns: 'appDebug' }).split('\n').map(item => (
<div key={item}>
{item}
</div>
)}
/>
))}
</Infotip>
</div>
<Listbox
value={languageItem}

View File

@ -1,11 +1,11 @@
import type { VariantProps } from 'class-variance-authority'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { cva } from 'class-variance-authority'
import * as React from 'react'
import { useCallback } from 'react'
import { FileTypeIcon } from '../file-uploader'
import { getFileAppearanceType } from '../file-uploader/utils'
import Tooltip from '../tooltip'
import ImageRender from './image-render'
const FileThumbVariants = cva(
@ -46,43 +46,49 @@ const FileThumb = ({
const { name, mimeType, sourceUrl } = file
const isImage = mimeType.startsWith('image/')
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
e.preventDefault()
onClick?.(file)
}, [onClick, file])
return (
<Tooltip
popupContent={name}
popupClassName="p-1.5 rounded-lg system-xs-medium text-text-secondary"
position="top"
>
<div
className={cn(
FileThumbVariants({ size, className }),
isImage
? 'p-px'
: 'rounded-md border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs hover:bg-components-panel-on-panel-item-bg-alt',
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label={name}
className={cn(
FileThumbVariants({ size, className }),
'border-0 bg-transparent p-0',
isImage
? 'p-px'
: 'rounded-md border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs hover:bg-components-panel-on-panel-item-bg-alt',
)}
onClick={handleClick}
>
{
isImage
? (
<ImageRender
sourceUrl={sourceUrl}
name={name}
/>
)
: (
<FileTypeIcon
type={getFileAppearanceType(name, mimeType)}
size="sm"
/>
)
}
</button>
)}
onClick={handleClick}
>
{
isImage
? (
<ImageRender
sourceUrl={sourceUrl}
name={name}
/>
)
: (
<FileTypeIcon
type={getFileAppearanceType(name, mimeType)}
size="sm"
/>
)
}
</div>
/>
<TooltipContent placement="top" className="rounded-lg p-1.5 system-xs-medium text-text-secondary">
{name}
</TooltipContent>
</Tooltip>
)
}

View File

@ -1,6 +1,6 @@
import { cn } from '@langgenius/dify-ui/cn'
import { useTranslation } from 'react-i18next'
import Tooltip from '../../tooltip'
import { Infotip } from '../../infotip'
export type LabelProps = {
htmlFor: string
@ -33,13 +33,9 @@ const Label = ({
{!isRequired && showOptional && <div className="ml-1 system-xs-regular text-text-tertiary">{t('label.optional', { ns: 'common' })}</div>}
{isRequired && <div className="ml-1 system-xs-regular text-text-destructive-secondary">*</div>}
{tooltip && (
<Tooltip
popupContent={
<div className="w-[200px]">{tooltip}</div>
}
triggerClassName="ml-0.5 w-4 h-4"
triggerTestId={`${htmlFor}-tooltip`}
/>
<Infotip aria-label={tooltip} className="ml-0.5 h-4 w-4" popupClassName="w-[200px]">
{tooltip}
</Infotip>
)}
</div>
)

View File

@ -1,11 +1,11 @@
'use client'
import type { InputProps } from '../input'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useClipboard } from '@/hooks/use-clipboard'
import ActionButton from '../action-button'
import Tooltip from '../tooltip'
type InputWithCopyProps = {
showCopyButton?: boolean
@ -64,18 +64,24 @@ const InputWithCopy = React.forwardRef<HTMLInputElement, InputWithCopyProps>((
onMouseLeave={reset}
data-testid="copy-button-wrapper"
>
<Tooltip
popupContent={safeTooltipText}
>
<ActionButton
size="xs"
onClick={handleCopy}
className="hover:bg-components-button-ghost-bg-hover"
>
{copied
? (<span className="i-ri-clipboard-fill h-3.5 w-3.5 text-text-tertiary" data-testid="copied-icon" />)
: (<span className="i-ri-clipboard-line h-3.5 w-3.5 text-text-tertiary" data-testid="copy-icon" />)}
</ActionButton>
<Tooltip>
<TooltipTrigger
render={(
<ActionButton
size="xs"
aria-label={safeTooltipText}
onClick={handleCopy}
className="hover:bg-components-button-ghost-bg-hover"
>
{copied
? (<span className="i-ri-clipboard-fill h-3.5 w-3.5 text-text-tertiary" data-testid="copied-icon" />)
: (<span className="i-ri-clipboard-line h-3.5 w-3.5 text-text-tertiary" data-testid="copy-icon" />)}
</ActionButton>
)}
/>
<TooltipContent>
{safeTooltipText}
</TooltipContent>
</Tooltip>
</div>
)}

View File

@ -1,3 +1,4 @@
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { RiInfoI } from '@remixicon/react'
import * as React from 'react'
@ -11,14 +12,20 @@ const Tooltip = ({
if (!content)
return null
return (
<div className="group relative z-10 size-[18px] overflow-visible">
<div className="absolute right-0 bottom-0 -z-10 hidden w-[260px] bg-saas-dify-blue-static px-5 py-[18px] system-xs-regular text-text-primary-on-surface group-hover:block">
{content}
</div>
<div className="flex h-full w-full items-center justify-center rounded-sm bg-state-base-hover transition-all duration-500 ease-in-out group-hover:rounded-none group-hover:bg-saas-dify-blue-static">
<Popover>
<PopoverTrigger
openOnHover
delay={0}
closeDelay={0}
aria-label={content}
className="group relative z-10 flex size-[18px] items-center justify-center rounded-sm border-0 bg-state-base-hover p-0 transition-[border-radius,background-color] duration-500 ease-in-out hover:rounded-none hover:bg-saas-dify-blue-static"
>
<RiInfoI className="size-3.5 text-text-tertiary group-hover:text-text-primary-on-surface" data-testid="tooltip-icon" />
</div>
</div>
</PopoverTrigger>
<PopoverContent placement="top-end" popupClassName="w-[260px] rounded-none border-0 bg-saas-dify-blue-static px-5 py-[18px] system-xs-regular text-text-primary-on-surface shadow-none">
{content}
</PopoverContent>
</Popover>
)
}

View File

@ -16,15 +16,16 @@ import {
DropdownMenuContent,
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { Switch } from '@langgenius/dify-ui/switch'
import { toast } from '@langgenius/dify-ui/toast'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useBoolean, useDebounceFn } from 'ahooks'
import { noop } from 'es-toolkit/function'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import Tooltip from '@/app/components/base/tooltip'
import { IS_CE_EDITION } from '@/config'
import { DataSourceType, DocumentActionType } from '@/models/datasets'
import { useRouter } from '@/next/navigation'
@ -205,11 +206,12 @@ const Operations = ({ embeddingAvailable, datasetId, detail, selectedIds, onSele
<>
{archived
? (
<Tooltip popupContent={t('list.action.enableWarning', { ns: 'datasetDocuments' })} popupClassName="!font-semibold">
<div>
<Switch checked={false} onCheckedChange={noop} disabled={true} size="md" />
</div>
</Tooltip>
<Popover>
<PopoverTrigger nativeButton={false} openOnHover render={<div><Switch checked={false} onCheckedChange={noop} disabled={true} size="md" /></div>} />
<PopoverContent popupClassName="px-3 py-2 font-semibold system-xs-regular text-text-tertiary">
{t('list.action.enableWarning', { ns: 'datasetDocuments' })}
</PopoverContent>
</Popover>
)
: <Switch checked={enabled} onCheckedChange={v => handleSwitch(v ? 'enable' : 'disable')} size="md" />}
<Divider className="!mr-2 !ml-4 !h-3" type="vertical" />
@ -217,16 +219,24 @@ const Operations = ({ embeddingAvailable, datasetId, detail, selectedIds, onSele
)}
{embeddingAvailable && (
<>
<Tooltip popupContent={t('list.action.settings', { ns: 'datasetDocuments' })} popupClassName="text-text-secondary system-xs-medium" needsDelay={false}>
<button
type="button"
className={cn('mr-2 cursor-pointer rounded-lg', !isListScene
? 'border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px] hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover'
: 'p-0.5 hover:bg-state-base-hover')}
onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}
>
<span aria-hidden className="i-ri-equalizer-2-line h-4 w-4 text-components-button-secondary-text" />
</button>
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label={t('list.action.settings', { ns: 'datasetDocuments' })}
className={cn('mr-2 cursor-pointer rounded-lg', !isListScene
? 'border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px] hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover'
: 'p-0.5 hover:bg-state-base-hover')}
onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}
>
<span aria-hidden className="i-ri-equalizer-2-line h-4 w-4 text-components-button-secondary-text" />
</button>
)}
/>
<TooltipContent className="system-xs-medium text-text-secondary">
{t('list.action.settings', { ns: 'datasetDocuments' })}
</TooltipContent>
</Tooltip>
<DropdownMenu open={isOperationsMenuOpen} onOpenChange={setIsOperationsMenuOpen}>
<DropdownMenuTrigger

View File

@ -1,12 +1,10 @@
import type { Placement } from '@floating-ui/react'
import type { OnlineDriveFile } from '@/models/pipeline'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Checkbox from '@/app/components/base/checkbox'
import Radio from '@/app/components/base/radio/ui'
import Tooltip from '@/app/components/base/tooltip'
import { formatFileSize } from '@/utils/format'
import FileIcon from './file-icon'
@ -33,14 +31,7 @@ const Item = ({
const isBucket = type === 'bucket'
const isFolder = type === 'folder'
const Wrapper = disabled ? Tooltip : React.Fragment
const wrapperProps = disabled
? {
popupContent: t('onlineDrive.notSupportedFileType', { ns: 'datasetPipeline' }),
position: 'top-end' as Placement,
offset: { mainAxis: 4, crossAxis: -104 },
}
: {}
const disabledTip = t('onlineDrive.notSupportedFileType', { ns: 'datasetPipeline' })
const handleSelect = useCallback((e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => {
e.stopPropagation()
@ -80,27 +71,44 @@ const Item = ({
onCheck={handleSelect}
/>
)}
<Wrapper
{...wrapperProps}
>
<div
className={cn(
'flex grow items-center gap-x-1 overflow-hidden py-0.5',
disabled && 'opacity-30',
{disabled
? (
<Popover>
<PopoverTrigger
openOnHover
aria-label={disabledTip}
className="flex grow items-center gap-x-1 overflow-hidden border-0 bg-transparent p-0 py-0.5 text-left opacity-30"
>
<FileIcon type={type} fileName={name} className="shrink-0 transform-gpu" />
<span
className="grow truncate system-sm-medium text-text-secondary"
title={name}
>
{name}
</span>
{!isFolder && typeof size === 'number' && (
<span className="shrink-0 system-xs-regular text-text-tertiary">{formatFileSize(size)}</span>
)}
</PopoverTrigger>
<PopoverContent placement="top-end" popupClassName="px-3 py-2 system-xs-regular text-text-tertiary">
{disabledTip}
</PopoverContent>
</Popover>
)
: (
<div className="flex grow items-center gap-x-1 overflow-hidden py-0.5">
<FileIcon type={type} fileName={name} className="shrink-0 transform-gpu" />
<span
className="grow truncate system-sm-medium text-text-secondary"
title={name}
>
{name}
</span>
{!isFolder && typeof size === 'number' && (
<span className="shrink-0 system-xs-regular text-text-tertiary">{formatFileSize(size)}</span>
)}
</div>
)}
>
<FileIcon type={type} fileName={name} className="shrink-0 transform-gpu" />
<span
className="grow truncate system-sm-medium text-text-secondary"
title={name}
>
{name}
</span>
{!isFolder && typeof size === 'number' && (
<span className="shrink-0 system-xs-regular text-text-tertiary">{formatFileSize(size)}</span>
)}
</div>
</Wrapper>
</div>
)
}

View File

@ -4,6 +4,7 @@ import type { InitialDocumentDetail } from '@/models/pipeline'
import type { RETRIEVE_METHOD } from '@/types/app'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import {
RiAedFill,
RiArrowRightLine,
@ -17,7 +18,6 @@ import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import NotionIcon from '@/app/components/base/notion-icon'
import Tooltip from '@/app/components/base/tooltip'
import PriorityLabel from '@/app/components/billing/priority-label'
import { Plan } from '@/app/components/billing/type'
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
@ -203,15 +203,18 @@ const EmbeddingProcess = ({
<div className="shrink-0 text-xs text-text-secondary">{`${getSourcePercent(indexingStatusDetail)}%`}</div>
)}
{indexingStatusDetail.indexing_status === 'error' && (
<Tooltip
popupClassName="px-4 py-[14px] max-w-60 body-xs-regular text-text-secondary border-[0.5px] border-components-panel-border rounded-xl"
offset={4}
popupContent={indexingStatusDetail.error}
>
<span>
<Popover>
<PopoverTrigger
openOnHover
aria-label={indexingStatusDetail.error}
className="inline-flex border-0 bg-transparent p-0"
>
<RiErrorWarningFill className="size-4 shrink-0 text-text-destructive" />
</span>
</Tooltip>
</PopoverTrigger>
<PopoverContent popupClassName="max-w-60 rounded-xl border-[0.5px] border-components-panel-border px-4 py-[14px] body-xs-regular text-text-secondary">
{indexingStatusDetail.error}
</PopoverContent>
</Popover>
)}
{indexingStatusDetail.indexing_status === 'completed' && (
<RiCheckboxCircleFill className="size-4 shrink-0 text-text-success" />

View File

@ -1,9 +1,9 @@
import type { FC } from 'react'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiLineHeight } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { Collapse } from '@/app/components/base/icons/src/vender/knowledge'
import Tooltip from '@/app/components/base/tooltip'
type DisplayToggleProps = {
isCollapsed: boolean
@ -15,25 +15,30 @@ const DisplayToggle: FC<DisplayToggleProps> = ({
toggleCollapsed,
}) => {
const { t } = useTranslation()
const label = isCollapsed ? t('segment.expandChunks', { ns: 'datasetDocuments' }) : t('segment.collapseChunks', { ns: 'datasetDocuments' })
return (
<Tooltip
popupContent={isCollapsed ? t('segment.expandChunks', { ns: 'datasetDocuments' }) : t('segment.collapseChunks', { ns: 'datasetDocuments' })}
popupClassName="text-text-secondary system-xs-medium border-[0.5px] border-components-panel-border"
>
<button
type="button"
className="flex items-center justify-center rounded-lg border-[0.5px] border-components-button-secondary-border
bg-components-button-secondary-bg p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px]"
onClick={toggleCollapsed}
>
{
isCollapsed
? <RiLineHeight className="h-4 w-4 text-components-button-secondary-text" />
: <Collapse className="h-4 w-4 text-components-button-secondary-text" />
}
</button>
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label={label}
className="flex items-center justify-center rounded-lg border-[0.5px] border-components-button-secondary-border
bg-components-button-secondary-bg p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px]"
onClick={toggleCollapsed}
>
{
isCollapsed
? <RiLineHeight className="h-4 w-4 text-components-button-secondary-text" />
: <Collapse className="h-4 w-4 text-components-button-secondary-text" />
}
</button>
)}
/>
<TooltipContent className="border-[0.5px] border-components-panel-border system-xs-medium text-text-secondary">
{label}
</TooltipContent>
</Tooltip>
)
}

View File

@ -10,13 +10,13 @@ import {
} from '@langgenius/dify-ui/alert-dialog'
import { cn } from '@langgenius/dify-ui/cn'
import { Switch } from '@langgenius/dify-ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiDeleteBinLine, RiEditLine } from '@remixicon/react'
import * as React from 'react'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import Divider from '@/app/components/base/divider'
import Tooltip from '@/app/components/base/tooltip'
import ImageList from '@/app/components/datasets/common/image-list'
import { ChunkingMode } from '@/models/datasets'
import { formatNumber } from '@/utils/format'
@ -182,35 +182,43 @@ const SegmentCard: FC<ISegmentCardProps> = ({
>
{!archived && (
<>
<Tooltip
popupContent="Edit"
popupClassName="text-text-secondary system-xs-medium"
>
<div
data-testid="segment-edit-button"
className="flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg hover:bg-state-base-hover"
onClick={(e) => {
e.stopPropagation()
onClickEdit?.()
}}
>
<RiEditLine className="h-4 w-4 text-text-tertiary" />
</div>
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label="Edit"
data-testid="segment-edit-button"
className="flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg hover:bg-state-base-hover"
onClick={(e) => {
e.stopPropagation()
onClickEdit?.()
}}
>
<RiEditLine className="h-4 w-4 text-text-tertiary" />
</button>
)}
/>
<TooltipContent className="system-xs-medium text-text-secondary">Edit</TooltipContent>
</Tooltip>
<Tooltip
popupContent="Delete"
popupClassName="text-text-secondary system-xs-medium"
>
<div
data-testid="segment-delete-button"
className="group/delete flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg hover:bg-state-destructive-hover"
onClick={(e) => {
e.stopPropagation()
setShowModal(true)
}}
>
<RiDeleteBinLine className="h-4 w-4 text-text-tertiary group-hover/delete:text-text-destructive" />
</div>
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label="Delete"
data-testid="segment-delete-button"
className="group/delete flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg hover:bg-state-destructive-hover"
onClick={(e) => {
e.stopPropagation()
setShowModal(true)
}}
>
<RiDeleteBinLine className="h-4 w-4 text-text-tertiary group-hover/delete:text-text-destructive" />
</button>
)}
/>
<TooltipContent className="system-xs-medium text-text-secondary">Delete</TooltipContent>
</Tooltip>
<Divider type="vertical" className="h-3.5 bg-divider-regular" />
</>

View File

@ -3,7 +3,6 @@ import type { FC } from 'react'
import type { BuiltInMetadataItem, MetadataItemInBatchEdit, MetadataItemWithEdit } from '../types'
import { Button } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { RiQuestionLine } from '@remixicon/react'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback, useState } from 'react'
@ -11,8 +10,8 @@ import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import { useCreateMetaData } from '@/service/knowledge/use-metadata'
import Checkbox from '../../../base/checkbox'
import { Infotip } from '../../../base/infotip'
import Modal from '../../../base/modal'
import Tooltip from '../../../base/tooltip'
import AddMetadataButton from '../add-metadata-button'
import useCheckMetadataName from '../hooks/use-check-metadata-name'
import SelectMetadataModal from '../metadata-dataset/select-metadata-modal'
@ -115,11 +114,14 @@ const EditMetadataBatchModal: FC<Props> = ({ datasetId, documentNum, list, onSav
<div className="flex items-center select-none">
<Checkbox checked={isApplyToAllSelectDocument} onCheck={() => setIsApplyToAllSelectDocument(!isApplyToAllSelectDocument)} id="apply-to-all" />
<div className="mr-1 ml-2 system-xs-medium text-text-secondary">{t(`${i18nPrefix}.applyToAllSelectDocument`, { ns: 'dataset' })}</div>
<Tooltip popupContent={<div className="max-w-[240px]">{t(`${i18nPrefix}.applyToAllSelectDocumentTip`, { ns: 'dataset' })}</div>}>
<div className="cursor-pointer p-px">
<RiQuestionLine className="size-3.5 text-text-tertiary" />
</div>
</Tooltip>
<Infotip
aria-label={t(`${i18nPrefix}.applyToAllSelectDocumentTip`, { ns: 'dataset' })}
className="p-px"
iconClassName="size-3.5 text-text-tertiary"
popupClassName="max-w-[240px]"
>
{t(`${i18nPrefix}.applyToAllSelectDocumentTip`, { ns: 'dataset' })}
</Infotip>
</div>
<div className="flex items-center space-x-2">
<Button onClick={onHide}>

View File

@ -20,9 +20,9 @@ import * as React from 'react'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Drawer from '@/app/components/base/drawer'
import { Infotip } from '@/app/components/base/infotip'
import Input from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
import Tooltip from '@/app/components/base/tooltip'
import CreateModal from '@/app/components/datasets/metadata/metadata-dataset/create-metadata-modal'
import { getIcon } from '../utils/get-icon'
import Field from './field'
@ -215,7 +215,9 @@ const DatasetMetadataDrawer: FC<Props> = ({
onCheckedChange={onIsBuiltInEnabledChange}
/>
<div className="mr-0.5 ml-2 system-sm-semibold text-text-secondary">{t(`${i18nPrefix}.builtIn`, { ns: 'dataset' })}</div>
<Tooltip popupContent={<div className="max-w-[100px]">{t(`${i18nPrefix}.builtInDescription`, { ns: 'dataset' })}</div>} />
<Infotip aria-label={t(`${i18nPrefix}.builtInDescription`, { ns: 'dataset' })} popupClassName="max-w-[100px]">
{t(`${i18nPrefix}.builtInDescription`, { ns: 'dataset' })}
</Infotip>
</div>
<div className="mt-1 space-y-1">

View File

@ -5,6 +5,7 @@ import type {
import {
Button,
} from '@langgenius/dify-ui/button'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import {
RiEqualizer2Line,
} from '@remixicon/react'
@ -13,7 +14,6 @@ import {
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import Authorized from './authorized'
import { useCredentialStatus } from './hooks'
@ -53,11 +53,11 @@ const ConfigProvider = ({
)
if (notAllowCustomCredential && !hasCredential) {
return (
<Tooltip
asChild
popupContent={t('auth.credentialUnavailable', { ns: 'plugin' })}
>
{Item}
<Tooltip>
<TooltipTrigger render={Item} />
<TooltipContent>
{t('auth.credentialUnavailable', { ns: 'plugin' })}
</TooltipContent>
</Tooltip>
)
}

View File

@ -6,6 +6,7 @@ import type {
} from '../declarations'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiArrowDownSLine } from '@remixicon/react'
import {
memo,
@ -13,7 +14,6 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import Tooltip from '@/app/components/base/tooltip'
import { ConfigurationMethodEnum, ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import Indicator from '@/app/components/header/indicator'
import Authorized from './authorized'
@ -89,11 +89,11 @@ const SwitchCredentialInLoadBalancing = ({
)
if (empty && notAllowCustomCredential) {
return (
<Tooltip
asChild
popupContent={t('auth.credentialUnavailable', { ns: 'plugin' })}
>
{Item}
<Tooltip>
<TooltipTrigger render={Item} />
<TooltipContent>
{t('auth.credentialUnavailable', { ns: 'plugin' })}
</TooltipContent>
</Tooltip>
)
}

View File

@ -1,5 +1,6 @@
import type { ReactNode } from 'react'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { RiErrorWarningFill } from '@remixicon/react'
import Tooltip from '@/app/components/base/tooltip'
import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version'
import Link from '@/next/link'
import { useInstalledPluginList } from '@/service/use-plugins'
@ -13,6 +14,28 @@ type StatusIndicatorsProps = {
t: any
}
type StatusPopoverProps = {
ariaLabel: string
content: ReactNode
children: ReactNode
}
const StatusPopover = ({ ariaLabel, content, children }: StatusPopoverProps) => (
<Popover>
<PopoverTrigger
openOnHover
aria-label={ariaLabel}
className="inline-flex border-0 bg-transparent p-0"
onClick={e => e.stopPropagation()}
>
{children}
</PopoverTrigger>
<PopoverContent placement="top" popupClassName="rounded-md px-3 py-2 system-xs-regular text-text-tertiary">
{content}
</PopoverContent>
</Popover>
)
const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disabled, pluginInfo, t }: StatusIndicatorsProps) => {
const { data: pluginList } = useInstalledPluginList()
const renderTooltipContent = (title: string, description?: string, linkText?: string, linkHref?: string) => {
@ -48,27 +71,26 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disa
<>
{inModelList
? (
<Tooltip
popupContent={t('nodes.agent.modelSelectorTooltips.deprecated', { ns: 'workflow' })}
asChild={false}
needsDelay={false}
<StatusPopover
ariaLabel={t('nodes.agent.modelSelectorTooltips.deprecated', { ns: 'workflow' })}
content={t('nodes.agent.modelSelectorTooltips.deprecated', { ns: 'workflow' })}
>
<RiErrorWarningFill className="h-4 w-4 text-text-destructive" />
</Tooltip>
</StatusPopover>
)
: !pluginInfo
? (
<Tooltip
popupContent={renderTooltipContent(
<StatusPopover
ariaLabel={t('nodes.agent.modelNotSupport.title', { ns: 'workflow' })}
content={renderTooltipContent(
t('nodes.agent.modelNotSupport.title', { ns: 'workflow' }),
t('nodes.agent.modelNotSupport.desc', { ns: 'workflow' }),
t('nodes.agent.linkToPlugin', { ns: 'workflow' }),
'/plugins',
)}
asChild={false}
>
<RiErrorWarningFill className="h-4 w-4 text-text-destructive" />
</Tooltip>
</StatusPopover>
)
: (
<SwitchPluginVersion
@ -82,17 +104,17 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disa
</>
)}
{!modelProvider && !pluginInfo && (
<Tooltip
popupContent={renderTooltipContent(
<StatusPopover
ariaLabel={t('nodes.agent.modelNotInMarketplace.title', { ns: 'workflow' })}
content={renderTooltipContent(
t('nodes.agent.modelNotInMarketplace.title', { ns: 'workflow' }),
t('nodes.agent.modelNotInMarketplace.desc', { ns: 'workflow' }),
t('nodes.agent.linkToPlugin', { ns: 'workflow' }),
'/plugins',
)}
asChild={false}
>
<RiErrorWarningFill className="h-4 w-4 text-text-destructive" />
</Tooltip>
</StatusPopover>
)}
</>
)

View File

@ -1,5 +1,6 @@
import type { ModelItem, ModelProvider } from '../declarations'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { Switch } from '@langgenius/dify-ui/switch'
import { useQueryClient } from '@tanstack/react-query'
import { useDebounceFn } from 'ahooks'
@ -7,7 +8,6 @@ import { memo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import Tooltip from '@/app/components/base/tooltip'
import { Plan } from '@/app/components/billing/type'
import { useAppContext } from '@/context/app-context'
import { useProviderContext, useProviderContextSelector } from '@/context/provider-context'
@ -102,14 +102,12 @@ const ModelListItem = ({ model, provider, isConfigurable, onChange, onModifyLoad
{
model.deprecated
? (
<Tooltip
popupContent={
<span className="font-semibold">{t('modelProvider.modelHasBeenDeprecated', { ns: 'common' })}</span>
}
offset={{ mainAxis: 4 }}
>
<Switch checked={false} disabled size="md" />
</Tooltip>
<Popover>
<PopoverTrigger nativeButton={false} openOnHover render={<span><Switch checked={false} disabled size="md" /></span>} />
<PopoverContent popupClassName="px-3 py-2 font-semibold system-xs-regular text-text-tertiary">
{t('modelProvider.modelHasBeenDeprecated', { ns: 'common' })}
</PopoverContent>
</Popover>
)
: (isCurrentWorkspaceManager && (
<Switch

View File

@ -2,6 +2,7 @@
import type { PluginDetail } from '@/app/components/plugins/types'
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import {
RiDeleteBinLine,
RiEditLine,
@ -10,7 +11,6 @@ import {
import { useBoolean } from 'ahooks'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Tooltip from '@/app/components/base/tooltip'
import { DeleteConfirm } from './delete-confirm'
import { EditModal } from './edit'
@ -65,19 +65,26 @@ const SubscriptionCard = ({ data, pluginDetail }: Props) => {
</div>
<div className="mt-1 flex items-center justify-between">
<Tooltip
disabled={!data.endpoint}
popupContent={data.endpoint && (
<div className="max-w-[320px] break-all">
{data.endpoint}
</div>
)}
position="left"
>
<div className="flex-1 truncate system-xs-regular text-text-tertiary">
{data.endpoint}
</div>
</Tooltip>
{data.endpoint
? (
<Popover>
<PopoverTrigger
openOnHover
aria-label={data.endpoint}
className="flex-1 truncate border-0 bg-transparent p-0 text-left system-xs-regular text-text-tertiary"
>
{data.endpoint}
</PopoverTrigger>
<PopoverContent placement="left" popupClassName="max-w-[320px] break-all px-3 py-2 system-xs-regular text-text-tertiary">
{data.endpoint}
</PopoverContent>
</Popover>
)
: (
<div className="flex-1 truncate system-xs-regular text-text-tertiary">
{data.endpoint}
</div>
)}
<div className="mx-2 text-xs text-text-tertiary opacity-30">·</div>
<div className="shrink-0 system-xs-regular text-text-tertiary">
{data.workflows_in_use > 0 ? t('subscription.list.item.usedByNum', { ns: 'pluginTrigger', num: data.workflows_in_use }) : t('subscription.list.item.noUsed', { ns: 'pluginTrigger' })}

View File

@ -9,6 +9,7 @@ import type {
import { cn } from '@langgenius/dify-ui/cn'
import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger } from '@langgenius/dify-ui/select'
import { Switch } from '@langgenius/dify-ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import {
RiArrowRightUpLine,
RiBracesLine,
@ -16,9 +17,8 @@ import {
import { useBoolean } from 'ahooks'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Infotip } from '@/app/components/base/infotip'
import Input from '@/app/components/base/input'
// eslint-disable-next-line no-restricted-imports -- legacy tooltip migration is handled separately from this change
import Tooltip from '@/app/components/base/tooltip'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { AppSelector } from '@/app/components/plugins/plugin-detail-panel/app-selector'
@ -127,17 +127,16 @@ const ReasoningConfigForm: React.FC<Props> = ({
} = schema
const auto = value[variable]?.auto
const fieldTitle = getFieldTitle(label, language)
const tooltipContent = (tooltip && (
<Tooltip
popupContent={(
<div className="w-[200px]">
{tooltip[language] || tooltip.en_US}
</div>
)}
triggerClassName="ml-0.5 w-4 h-4"
asChild={false}
/>
))
const tooltipText = tooltip?.[language] || tooltip?.en_US
const tooltipContent = tooltipText && (
<Infotip
aria-label={tooltipText}
className="ml-0.5 h-4 w-4"
popupClassName="w-[200px]"
>
{tooltipText}
</Infotip>
)
const varInput = value[variable]!.value
const {
isString,
@ -173,20 +172,22 @@ const ReasoningConfigForm: React.FC<Props> = ({
<span className="mx-1 system-xs-regular text-text-quaternary">·</span>
<span className="system-xs-regular text-text-tertiary">{resolveTargetVarType(type)}</span>
{isShowJSONEditor && (
<Tooltip
popupContent={(
<div className="system-xs-medium text-text-secondary">
{t('nodes.agent.clickToViewParameterSchema', { ns: 'workflow' })}
</div>
)}
asChild={false}
>
<div
className="ml-0.5 cursor-pointer rounded-sm p-px text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
onClick={() => showSchema(input_schema as SchemaRoot, fieldTitle!)}
>
<RiBracesLine className="size-3.5" />
</div>
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label={t('nodes.agent.clickToViewParameterSchema', { ns: 'workflow' })}
className="ml-0.5 cursor-pointer rounded-sm border-0 bg-transparent p-px text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
onClick={() => showSchema(input_schema as SchemaRoot, fieldTitle!)}
>
<RiBracesLine className="size-3.5" />
</button>
)}
/>
<TooltipContent className="system-xs-medium text-text-secondary">
{t('nodes.agent.clickToViewParameterSchema', { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
)}

View File

@ -1,8 +1,8 @@
'use client'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { Switch } from '@langgenius/dify-ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import {
RiDeleteBinLine,
RiEqualizer2Line,
@ -14,7 +14,6 @@ import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import AppIcon from '@/app/components/base/app-icon'
import { Group } from '@/app/components/base/icons/src/vender/other'
import { ToolTipContent } from '@/app/components/base/tooltip/content'
import Indicator from '@/app/components/header/indicator'
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
@ -144,11 +143,14 @@ const ToolItem = ({
className="-mt-1"
uniqueIdentifier={installInfo}
tooltip={(
<ToolTipContent
title={t('detailPanel.toolSelector.unsupportedTitle', { ns: 'plugin' })}
>
{`${t('detailPanel.toolSelector.unsupportedContent', { ns: 'plugin' })} ${t('detailPanel.toolSelector.unsupportedContent2', { ns: 'plugin' })}`}
</ToolTipContent>
<div className="w-[180px]" data-testid="tooltip-content">
<div className="mb-1.5 font-semibold text-text-secondary" data-testid="tooltip-content-title">
{t('detailPanel.toolSelector.unsupportedTitle', { ns: 'plugin' })}
</div>
<div className="mb-1.5 text-text-tertiary" data-testid="tooltip-content-body">
{`${t('detailPanel.toolSelector.unsupportedContent', { ns: 'plugin' })} ${t('detailPanel.toolSelector.unsupportedContent2', { ns: 'plugin' })}`}
</div>
</div>
)}
onChange={() => {
onInstall?.()
@ -167,18 +169,18 @@ const ToolItem = ({
/>
)}
{isError && (
<Tooltip>
<TooltipTrigger
render={(
<div aria-label={typeof errorTip === 'string' ? errorTip : undefined}>
<RiErrorWarningFill className="h-4 w-4 text-text-destructive" />
</div>
)}
/>
<TooltipContent>
<Popover>
<PopoverTrigger
openOnHover
aria-label={typeof errorTip === 'string' ? errorTip : t('detailPanel.toolSelector.unsupportedTitle', { ns: 'plugin' })}
className="inline-flex border-0 bg-transparent p-0"
>
<RiErrorWarningFill className="h-4 w-4 text-text-destructive" />
</PopoverTrigger>
<PopoverContent popupClassName="px-3 py-2 system-xs-regular text-text-tertiary">
{errorTip}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)}
</div>
)

View File

@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { PluginDetail } from '../types'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import {
RiArrowRightUpLine,
RiBugLine,
@ -13,7 +14,6 @@ import { useSuspenseQuery } from '@tanstack/react-query'
import * as React from 'react'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
import { API_PREFIX } from '@/config'
import { useAppContext } from '@/context/app-context'
@ -124,12 +124,18 @@ const PluginItem: FC<Props> = ({
<Title title={title} />
{verified && <Verified className="ml-0.5 h-4 w-4" text={t('marketplace.verifiedTip', { ns: 'plugin' })} />}
{!isDifyVersionCompatible && (
<Tooltip popupContent={
t('difyVersionNotCompatible', { ns: 'plugin', minimalDifyVersion: declarationMeta.minimum_dify_version })
}
>
<RiErrorWarningLine color="red" className="ml-0.5 h-4 w-4 shrink-0 text-text-accent" />
</Tooltip>
<Popover>
<PopoverTrigger
openOnHover
aria-label={t('difyVersionNotCompatible', { ns: 'plugin', minimalDifyVersion: declarationMeta.minimum_dify_version })}
className="ml-0.5 inline-flex h-4 w-4 shrink-0 border-0 bg-transparent p-0"
>
<RiErrorWarningLine color="red" className="h-4 w-4 text-text-accent" />
</PopoverTrigger>
<PopoverContent popupClassName="px-3 py-2 system-xs-regular text-text-tertiary">
{t('difyVersionNotCompatible', { ns: 'plugin', minimalDifyVersion: declarationMeta.minimum_dify_version })}
</PopoverContent>
</Popover>
)}
<Badge
className="ml-1 shrink-0"

View File

@ -13,7 +13,7 @@ import {
import { useTranslation } from 'react-i18next'
import { useNodes } from 'reactflow'
import Divider from '@/app/components/base/divider'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks'
import { useNodesSyncDraft } from '@/app/components/workflow/hooks'
import { useStore } from '@/app/components/workflow/store'
@ -137,10 +137,12 @@ const InputFieldPanel = () => {
<span className="system-sm-semibold-uppercase text-text-secondary">
{t('inputFieldPanel.uniqueInputs.title', { ns: 'datasetPipeline' })}
</span>
<Tooltip
popupContent={t('inputFieldPanel.uniqueInputs.tooltip', { ns: 'datasetPipeline' })}
<Infotip
aria-label={t('inputFieldPanel.uniqueInputs.tooltip', { ns: 'datasetPipeline' })}
popupClassName="max-w-[240px]"
/>
>
{t('inputFieldPanel.uniqueInputs.tooltip', { ns: 'datasetPipeline' })}
</Infotip>
</div>
<div className="flex flex-col gap-y-1 py-1">
{

View File

@ -6,9 +6,9 @@ import { cn } from '@langgenius/dify-ui/cn'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Drawer from '@/app/components/base/drawer-plus'
import { Infotip } from '@/app/components/base/infotip'
import Input from '@/app/components/base/input'
import Radio from '@/app/components/base/radio/ui'
import Tooltip from '@/app/components/base/tooltip'
import { AuthHeaderPrefix, AuthType } from '@/app/components/tools/types'
type Props = {
@ -123,14 +123,13 @@ const ConfigCredential: FC<Props> = ({
<div>
<div className="flex items-center py-2 system-sm-medium text-text-primary">
{t('createTool.authMethod.key', { ns: 'tools' })}
<Tooltip
popupContent={(
<div className="w-[261px] text-text-tertiary">
{t('createTool.authMethod.keyTooltip', { ns: 'tools' })}
</div>
)}
triggerClassName="ml-0.5 w-4 h-4"
/>
<Infotip
aria-label={t('createTool.authMethod.keyTooltip', { ns: 'tools' })}
className="ml-0.5 h-4 w-4"
popupClassName="w-[261px] text-text-tertiary"
>
{t('createTool.authMethod.keyTooltip', { ns: 'tools' })}
</Infotip>
</div>
<Input
value={tempCredential.api_key_header}
@ -153,14 +152,13 @@ const ConfigCredential: FC<Props> = ({
<div>
<div className="flex items-center py-2 system-sm-medium text-text-primary">
{t('createTool.authMethod.queryParam', { ns: 'tools' })}
<Tooltip
popupContent={(
<div className="w-[261px] text-text-tertiary">
{t('createTool.authMethod.queryParamTooltip', { ns: 'tools' })}
</div>
)}
triggerClassName="ml-0.5 w-4 h-4"
/>
<Infotip
aria-label={t('createTool.authMethod.queryParamTooltip', { ns: 'tools' })}
className="ml-0.5 h-4 w-4"
popupClassName="w-[261px] text-text-tertiary"
>
{t('createTool.authMethod.queryParamTooltip', { ns: 'tools' })}
</Infotip>
</div>
<Input
value={tempCredential.api_key_query_param}

View File

@ -6,10 +6,10 @@ import type {
ToolWithProvider,
} from '../types'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useSuspenseQuery } from '@tanstack/react-query'
import { memo, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { systemFeaturesQueryOptions } from '@/service/system-features'
import { useFeaturedToolsRecommendations } from '@/service/use-plugins'
import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools } from '@/service/use-tools'
@ -129,19 +129,22 @@ const TabHeaderItem = ({
if (tab.disabled) {
return (
<Tooltip
key={tab.key}
position="top"
popupClassName="max-w-[200px]"
popupContent={disabledTip}
>
<div
className={className}
aria-disabled={tab.disabled}
onClick={handleClick}
>
{tab.name}
</div>
<Tooltip key={tab.key}>
<TooltipTrigger
render={(
<button
type="button"
className={className}
aria-disabled={tab.disabled}
onClick={handleClick}
>
{tab.name}
</button>
)}
/>
<TooltipContent placement="top" className="max-w-[200px]">
{disabledTip}
</TooltipContent>
</Tooltip>
)
}

View File

@ -1,22 +1,23 @@
'use client'
import type { FC } from 'react'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { RiAlertFill } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
const McpToolNotSupportTooltip: FC = () => {
const { t } = useTranslation()
const tip = t('detailPanel.toolSelector.unsupportedMCPTool', { ns: 'plugin' })
return (
<Tooltip
popupContent={(
<div className="w-[256px]">
{t('detailPanel.toolSelector.unsupportedMCPTool', { ns: 'plugin' })}
</div>
)}
>
<RiAlertFill className="size-4 text-text-warning-secondary" />
</Tooltip>
<Popover>
<PopoverTrigger openOnHover aria-label={tip} className="inline-flex border-0 bg-transparent p-0">
<RiAlertFill className="size-4 text-text-warning-secondary" />
</PopoverTrigger>
<PopoverContent popupClassName="w-[256px] px-3 py-2 system-xs-regular text-text-tertiary">
{tip}
</PopoverContent>
</Popover>
)
}
export default React.memo(McpToolNotSupportTooltip)

View File

@ -2,13 +2,13 @@
import type { FC, ReactNode } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { RiArrowLeftRightLine, RiExternalLinkLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index'
import Tooltip from '@/app/components/base/tooltip'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
import PluginMutationModel from '@/app/components/plugins/plugin-mutation-model'
@ -67,76 +67,91 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
if (!uniqueIdentifier || !pluginId)
return null
const content = (
<div className={cn('flex w-fit items-center justify-center', className)} onClick={e => e.stopPropagation()}>
{isShowUpdateModal && pluginDetail && (
<PluginMutationModel
onCancel={hideUpdateModal}
plugin={pluginManifestToCardPluginProps({
...pluginDetail.declaration,
icon: icon!,
})}
mutation={mutation}
mutate={install}
confirmButtonText={t('nodes.agent.installPlugin.install', { ns: 'workflow' })}
cancelButtonText={t('nodes.agent.installPlugin.cancel', { ns: 'workflow' })}
modelTitle={t('nodes.agent.installPlugin.title', { ns: 'workflow' })}
description={t('nodes.agent.installPlugin.desc', { ns: 'workflow' })}
cardTitleLeft={(
<>
<Badge2 className="mx-1" size="s" state={BadgeState.Warning}>
{`${pluginDetail.version} -> ${target!.version}`}
</Badge2>
</>
)}
modalBottomLeft={(
<Link
className="flex items-center justify-center gap-1"
href={getMarketplaceUrl(`/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`)}
target="_blank"
rel="noopener noreferrer"
>
<span className="system-xs-regular text-xs text-text-accent">
{t('nodes.agent.installPlugin.changelog', { ns: 'workflow' })}
</span>
<RiExternalLinkLine className="size-3 text-text-accent" />
</Link>
)}
/>
)}
{pluginDetail && (
<PluginVersionPicker
isShow={isShow}
onShowChange={setIsShow}
pluginID={pluginId}
currentVersion={pluginDetail.version}
onSelect={(state) => {
setTarget({
pluginUniqueIden: state.unique_identifier,
version: state.version,
})
showUpdateModal()
}}
trigger={(
<Badge
className={cn(
'mx-1 flex hover:bg-state-base-hover',
isShow && 'bg-state-base-hover',
)}
uppercase={true}
text={(
<>
<div>{pluginDetail.version}</div>
<RiArrowLeftRightLine className="ml-1 h-3 w-3 text-text-tertiary" />
</>
)}
hasRedCornerMark={true}
/>
)}
/>
)}
</div>
)
if (!tooltip || isShow || isShowUpdateModal)
return content
return (
<Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod="hover">
<div className={cn('flex w-fit items-center justify-center', className)} onClick={e => e.stopPropagation()}>
{isShowUpdateModal && pluginDetail && (
<PluginMutationModel
onCancel={hideUpdateModal}
plugin={pluginManifestToCardPluginProps({
...pluginDetail.declaration,
icon: icon!,
})}
mutation={mutation}
mutate={install}
confirmButtonText={t('nodes.agent.installPlugin.install', { ns: 'workflow' })}
cancelButtonText={t('nodes.agent.installPlugin.cancel', { ns: 'workflow' })}
modelTitle={t('nodes.agent.installPlugin.title', { ns: 'workflow' })}
description={t('nodes.agent.installPlugin.desc', { ns: 'workflow' })}
cardTitleLeft={(
<>
<Badge2 className="mx-1" size="s" state={BadgeState.Warning}>
{`${pluginDetail.version} -> ${target!.version}`}
</Badge2>
</>
)}
modalBottomLeft={(
<Link
className="flex items-center justify-center gap-1"
href={getMarketplaceUrl(`/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`)}
target="_blank"
rel="noopener noreferrer"
>
<span className="system-xs-regular text-xs text-text-accent">
{t('nodes.agent.installPlugin.changelog', { ns: 'workflow' })}
</span>
<RiExternalLinkLine className="size-3 text-text-accent" />
</Link>
)}
/>
)}
{pluginDetail && (
<PluginVersionPicker
isShow={isShow}
onShowChange={setIsShow}
pluginID={pluginId}
currentVersion={pluginDetail.version}
onSelect={(state) => {
setTarget({
pluginUniqueIden: state.unique_identifier,
version: state.version,
})
showUpdateModal()
}}
trigger={(
<Badge
className={cn(
'mx-1 flex hover:bg-state-base-hover',
isShow && 'bg-state-base-hover',
)}
uppercase={true}
text={(
<>
<div>{pluginDetail.version}</div>
<RiArrowLeftRightLine className="ml-1 h-3 w-3 text-text-tertiary" />
</>
)}
hasRedCornerMark={true}
/>
)}
/>
)}
</div>
</Tooltip>
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={typeof tooltip === 'string' ? tooltip : t('nodes.agent.installPlugin.title', { ns: 'workflow' })}
render={content}
/>
<PopoverContent popupClassName="px-3 py-2 system-xs-regular text-text-tertiary">
{tooltip}
</PopoverContent>
</Popover>
)
}

View File

@ -1,8 +1,8 @@
import type { NodeProps } from 'reactflow'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiHome5Fill } from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { NodeSourceHandle } from '@/app/components/workflow/nodes/_base/components/node-handle'
const IterationStartNode = ({ id, data }: NodeProps) => {
@ -10,10 +10,14 @@ const IterationStartNode = ({ id, data }: NodeProps) => {
return (
<div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg shadow-xs">
<Tooltip popupContent={t('blocks.iteration-start', { ns: 'workflow' })} asChild={false}>
<div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500">
<Tooltip>
<TooltipTrigger
aria-label={t('blocks.iteration-start', { ns: 'workflow' })}
className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500 p-0"
>
<RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" />
</div>
</TooltipTrigger>
<TooltipContent>{t('blocks.iteration-start', { ns: 'workflow' })}</TooltipContent>
</Tooltip>
<NodeSourceHandle
id={id}
@ -30,10 +34,14 @@ export const IterationStartNodeDumb = () => {
return (
<div className="nodrag relative top-[21px] left-[17px] z-11 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg">
<Tooltip popupContent={t('blocks.iteration-start', { ns: 'workflow' })} asChild={false}>
<div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500">
<Tooltip>
<TooltipTrigger
aria-label={t('blocks.iteration-start', { ns: 'workflow' })}
className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500 p-0"
>
<RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" />
</div>
</TooltipTrigger>
<TooltipContent>{t('blocks.iteration-start', { ns: 'workflow' })}</TooltipContent>
</Tooltip>
</div>
)

View File

@ -17,7 +17,7 @@ import {
import { useTranslation } from 'react-i18next'
import WeightedScoreComponent from '@/app/components/app/configuration/dataset-config/params-config/weighted-score'
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import { DEFAULT_WEIGHTED_SCORE } from '@/models/datasets'
import {
HybridSearchModeEnum,
@ -174,10 +174,13 @@ const SearchMethodOption = ({
disabled={readonly}
/>
{t('modelProvider.rerankModel.key', { ns: 'common' })}
<Tooltip
triggerClassName="ml-0.5 shrink-0 w-3.5 h-3.5"
popupContent={t('modelProvider.rerankModel.tip', { ns: 'common' })}
/>
<Infotip
aria-label={t('modelProvider.rerankModel.tip', { ns: 'common' })}
className="ml-0.5 h-3.5 w-3.5 shrink-0"
iconClassName="h-3.5 w-3.5"
>
{t('modelProvider.rerankModel.tip', { ns: 'common' })}
</Infotip>
</div>
)
}

View File

@ -5,7 +5,7 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import Collapse from '@/app/components/workflow/nodes/_base/components/collapse'
import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types'
@ -46,13 +46,9 @@ const MetadataFilter = ({
<div className="mr-0.5 system-sm-semibold-uppercase text-text-secondary">
{t('nodes.knowledgeRetrieval.metadata.title', { ns: 'workflow' })}
</div>
<Tooltip
popupContent={(
<div className="w-[200px]">
{t('nodes.knowledgeRetrieval.metadata.tip', { ns: 'workflow' })}
</div>
)}
/>
<Infotip aria-label={t('nodes.knowledgeRetrieval.metadata.tip', { ns: 'workflow' })} popupClassName="w-[200px]">
{t('nodes.knowledgeRetrieval.metadata.tip', { ns: 'workflow' })}
</Infotip>
{collapseIcon}
</div>
<div className="flex items-center">

View File

@ -1,8 +1,8 @@
import type { NodeProps } from 'reactflow'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiHome5Fill } from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { NodeSourceHandle } from '@/app/components/workflow/nodes/_base/components/node-handle'
const LoopStartNode = ({ id, data }: NodeProps) => {
@ -10,10 +10,14 @@ const LoopStartNode = ({ id, data }: NodeProps) => {
return (
<div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg">
<Tooltip popupContent={t('blocks.loop-start', { ns: 'workflow' })} asChild={false}>
<div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500">
<Tooltip>
<TooltipTrigger
aria-label={t('blocks.loop-start', { ns: 'workflow' })}
className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500 p-0"
>
<RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" />
</div>
</TooltipTrigger>
<TooltipContent>{t('blocks.loop-start', { ns: 'workflow' })}</TooltipContent>
</Tooltip>
<NodeSourceHandle
id={id}
@ -30,10 +34,14 @@ export const LoopStartNodeDumb = () => {
return (
<div className="nodrag relative top-[21px] left-[17px] z-11 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg">
<Tooltip popupContent={t('blocks.loop-start', { ns: 'workflow' })} asChild={false}>
<div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500">
<Tooltip>
<TooltipTrigger
aria-label={t('blocks.loop-start', { ns: 'workflow' })}
className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500 p-0"
>
<RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" />
</div>
</TooltipTrigger>
<TooltipContent>{t('blocks.loop-start', { ns: 'workflow' })}</TooltipContent>
</Tooltip>
</div>
)

View File

@ -3,7 +3,7 @@ import type { ParameterExtractorNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse'
import Field from '@/app/components/workflow/nodes/_base/components/field'
@ -131,14 +131,14 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({
title={(
<div className="flex items-center space-x-1">
<span className="uppercase">{t(`${i18nPrefix}.instruction`, { ns: 'workflow' })}</span>
<Tooltip
popupContent={(
<div className="w-[120px]">
{t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
</div>
)}
triggerClassName="w-3.5 h-3.5 ml-0.5"
/>
<Infotip
aria-label={t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
className="ml-0.5 h-3.5 w-3.5"
iconClassName="h-3.5 w-3.5"
popupClassName="w-[120px]"
>
{t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
</Infotip>
</div>
)}
value={inputs.instruction}

View File

@ -3,7 +3,7 @@ import type { FC } from 'react'
import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
import MemoryConfig from '../../_base/components/memory-config'
@ -48,14 +48,14 @@ const AdvancedSetting: FC<Props> = ({
title={(
<div className="flex items-center space-x-1">
<span className="uppercase">{t(`${i18nPrefix}.instruction`, { ns: 'workflow' })}</span>
<Tooltip
popupContent={(
<div className="w-[120px]">
{t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
</div>
)}
triggerClassName="w-3.5 h-3.5 ml-0.5"
/>
<Infotip
aria-label={t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
className="ml-0.5 h-3.5 w-3.5"
iconClassName="h-3.5 w-3.5"
popupClassName="w-[120px]"
>
{t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
</Infotip>
</div>
)}
value={instruction}

View File

@ -2,9 +2,9 @@ import type { TFunction } from 'i18next'
import type { FC } from 'react'
import type { NodeProps } from 'reactflow'
import type { QuestionClassifierNodeType } from './types'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import {
useTextGenerationCurrentProviderAndModelAndModelList,
} from '@/app/components/header/account-setting/model-provider-page/hooks'
@ -47,15 +47,18 @@ const TruncatedClassItem: FC<TruncatedClassItemProps> = ({ topic, index, nodeId,
</div>
{shouldShowTooltip
? (
<Tooltip
popupContent={(
<div className="max-w-[300px] wrap-break-word">
<ReadonlyInputWithSelectVar value={topic.name} nodeId={nodeId} />
</div>
)}
>
{content}
</Tooltip>
<Popover>
<PopoverTrigger
openOnHover
aria-label={topic.name}
className="w-full border-0 bg-transparent p-0 text-left"
>
{content}
</PopoverTrigger>
<PopoverContent popupClassName="max-w-[300px] px-3 py-2 system-xs-regular wrap-break-word text-text-tertiary">
<ReadonlyInputWithSelectVar value={topic.name} nodeId={nodeId} />
</PopoverContent>
</Popover>
)
: content}
</div>

View File

@ -9,7 +9,7 @@ import {
RiBracesLine,
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { SchemaModal } from '@/app/components/plugins/plugin-detail-panel/tool-selector/components'
@ -57,15 +57,13 @@ const TriggerFormItem: FC<Props> = ({
<div className="ml-1 system-xs-regular text-text-destructive-secondary">*</div>
)}
{!showDescription && tooltip && (
<Tooltip
popupContent={(
<div className="w-[200px]">
{tooltip[language] || tooltip.en_US}
</div>
)}
triggerClassName="ml-1 w-4 h-4"
asChild={false}
/>
<Infotip
aria-label={tooltip[language] || tooltip.en_US}
className="ml-1 h-4 w-4"
popupClassName="w-[200px]"
>
{tooltip[language] || tooltip.en_US}
</Infotip>
)}
{showSchemaButton && (
<>

View File

@ -11,12 +11,12 @@ import {
} from '@langgenius/dify-ui/number-field'
import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger } from '@langgenius/dify-ui/select'
import { toast } from '@langgenius/dify-ui/toast'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import copy from 'copy-to-clipboard'
import * as React from 'react'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import InputWithCopy from '@/app/components/base/input-with-copy'
import Tooltip from '@/app/components/base/tooltip'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import OutputVars from '@/app/components/workflow/nodes/_base/components/output-vars'
import Split from '@/app/components/workflow/nodes/_base/components/split'
@ -118,32 +118,38 @@ const Panel: FC<NodePanelProps<WebhookTriggerNodeType>> = ({
</div>
{inputs.webhook_debug_url && (
<div className="space-y-2">
<Tooltip
popupContent={debugUrlCopied ? t(`${i18nPrefix}.debugUrlCopied`, { ns: 'workflow' }) : t(`${i18nPrefix}.debugUrlCopy`, { ns: 'workflow' })}
popupClassName="system-xs-regular text-text-primary bg-components-tooltip-bg border border-components-panel-border shadow-lg backdrop-blur-xs rounded-md px-1.5 py-1"
position="top"
offset={{ mainAxis: -20 }}
needsDelay={true}
>
<div
className="flex cursor-pointer gap-1.5 rounded-lg px-1 py-1.5 transition-colors"
style={{ width: '368px', height: '38px' }}
onClick={() => {
copy(inputs.webhook_debug_url || '')
setDebugUrlCopied(true)
setTimeout(() => setDebugUrlCopied(false), 2000)
}}
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label={t(`${i18nPrefix}.debugUrlCopy`, { ns: 'workflow' })}
className="flex cursor-pointer gap-1.5 rounded-lg px-1 py-1.5 text-left transition-colors"
style={{ width: '368px', height: '38px' }}
onClick={() => {
copy(inputs.webhook_debug_url || '')
setDebugUrlCopied(true)
setTimeout(() => setDebugUrlCopied(false), 2000)
}}
>
<span className="mt-0.5 w-0.5 bg-divider-regular" style={{ height: '28px' }} />
<span className="flex-1" style={{ width: '352px', height: '32px' }}>
<span className="block text-xs leading-4 text-text-tertiary">
{t(`${i18nPrefix}.debugUrlTitle`, { ns: 'workflow' })}
</span>
<span className="block truncate text-xs leading-4 text-text-primary">
{inputs.webhook_debug_url}
</span>
</span>
</button>
)}
/>
<TooltipContent
placement="top"
className="rounded-md border border-components-panel-border bg-components-tooltip-bg px-1.5 py-1 system-xs-regular text-text-primary shadow-lg backdrop-blur-xs"
>
<div className="mt-0.5 w-0.5 bg-divider-regular" style={{ height: '28px' }}></div>
<div className="flex-1" style={{ width: '352px', height: '32px' }}>
<div className="text-xs leading-4 text-text-tertiary">
{t(`${i18nPrefix}.debugUrlTitle`, { ns: 'workflow' })}
</div>
<div className="truncate text-xs leading-4 text-text-primary">
{inputs.webhook_debug_url}
</div>
</div>
</div>
{debugUrlCopied ? t(`${i18nPrefix}.debugUrlCopied`, { ns: 'workflow' }) : t(`${i18nPrefix}.debugUrlCopy`, { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
{isPrivateOrLocalAddress(inputs.webhook_debug_url) && (
<div className="mt-1 px-0 py-[2px] system-xs-regular text-text-warning">

View File

@ -7,8 +7,8 @@ import * as React from 'react'
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { v4 as uuid4 } from 'uuid'
import { Infotip } from '@/app/components/base/infotip'
import Input from '@/app/components/base/input'
import Tooltip from '@/app/components/base/tooltip'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
@ -129,14 +129,14 @@ const VariableModal = ({
onClick={() => setType('secret')}
>
<span>Secret</span>
<Tooltip
popupContent={(
<div className="w-[240px]">
{t('env.modal.secretTip', { ns: 'workflow' })}
</div>
)}
triggerClassName="ml-0.5 w-3.5 h-3.5"
/>
<Infotip
aria-label={t('env.modal.secretTip', { ns: 'workflow' })}
className="ml-0.5 h-3.5 w-3.5"
iconClassName="h-3.5 w-3.5"
popupClassName="w-[240px]"
>
{t('env.modal.secretTip', { ns: 'workflow' })}
</Infotip>
</div>
</div>
</div>

View File

@ -4,12 +4,12 @@ import type { Node } from 'reactflow'
import type { ScheduleTriggerNodeType } from '@/app/components/workflow/nodes/trigger-schedule/types'
import type { WebhookTriggerNodeType } from '@/app/components/workflow/nodes/trigger-webhook/types'
import { Button } from '@langgenius/dify-ui/button'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import copy from 'copy-to-clipboard'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import Tooltip from '@/app/components/base/tooltip'
import BlockIcon from '@/app/components/workflow/block-icon'
import { useGetToolIcon } from '@/app/components/workflow/hooks/use-tool-icon'
import { getNextExecutionTime } from '@/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator'
@ -179,28 +179,32 @@ const Listening: FC<ListeningProps> = ({
<div className="shrink-0 system-xs-regular whitespace-pre-line text-text-tertiary">
{t('nodes.triggerWebhook.debugUrlTitle', { ns: 'workflow' })}
</div>
<Tooltip
popupContent={debugUrlCopied
? t('nodes.triggerWebhook.debugUrlCopied', { ns: 'workflow' })
: t('nodes.triggerWebhook.debugUrlCopy', { ns: 'workflow' })}
popupClassName="system-xs-regular text-text-primary bg-components-tooltip-bg border border-components-panel-border shadow-lg backdrop-blur-xs rounded-md px-1.5 py-1"
position="top"
offset={{ mainAxis: -4 }}
needsDelay={true}
>
<button
type="button"
aria-label={t('nodes.triggerWebhook.debugUrlCopy', { ns: 'workflow' }) || ''}
className={`inline-flex items-center rounded-md border border-divider-regular bg-components-badge-white-to-dark px-1.5 py-[2px] font-mono text-[13px] leading-[18px] text-text-secondary transition-colors hover:bg-components-panel-on-panel-item-bg-hover focus:outline-hidden focus-visible:outline-2 focus-visible:outline-components-panel-border focus-visible:outline-solid ${debugUrlCopied ? 'bg-components-panel-on-panel-item-bg-hover text-text-primary' : ''}`}
onClick={() => {
copy(webhookDebugUrl)
setDebugUrlCopied(true)
}}
<Tooltip>
<TooltipTrigger
render={(
<button
type="button"
aria-label={t('nodes.triggerWebhook.debugUrlCopy', { ns: 'workflow' }) || ''}
className={`inline-flex items-center rounded-md border border-divider-regular bg-components-badge-white-to-dark px-1.5 py-[2px] font-mono text-[13px] leading-[18px] text-text-secondary transition-colors hover:bg-components-panel-on-panel-item-bg-hover focus:outline-hidden focus-visible:outline-2 focus-visible:outline-components-panel-border focus-visible:outline-solid ${debugUrlCopied ? 'bg-components-panel-on-panel-item-bg-hover text-text-primary' : ''}`}
onClick={() => {
copy(webhookDebugUrl)
setDebugUrlCopied(true)
}}
>
<span className="whitespace-nowrap text-text-primary">
{webhookDebugUrl}
</span>
</button>
)}
/>
<TooltipContent
placement="top"
className="rounded-md border border-components-panel-border bg-components-tooltip-bg px-1.5 py-1 system-xs-regular text-text-primary shadow-lg backdrop-blur-xs"
>
<span className="whitespace-nowrap text-text-primary">
{webhookDebugUrl}
</span>
</button>
{debugUrlCopied
? t('nodes.triggerWebhook.debugUrlCopied', { ns: 'workflow' })
: t('nodes.triggerWebhook.debugUrlCopy', { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
</div>
)}

View File

@ -6,12 +6,12 @@ import type {
NodeProps,
} from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import {
cloneElement,
memo,
} from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import BlockIcon from '@/app/components/workflow/block-icon'
import {
BlockEnum,
@ -91,19 +91,21 @@ const BaseCard = ({
</div>
{
data.type === BlockEnum.Iteration && (data as IterationNodeType).is_parallel && (
<Tooltip popupContent={(
<div className="w-[180px]">
<Popover>
<PopoverTrigger
openOnHover
aria-label={t('nodes.iteration.parallelModeEnableTitle', { ns: 'workflow' })}
className="ml-1 flex items-center justify-center rounded-[5px] border border-text-warning bg-transparent px-[5px] py-[3px] system-2xs-medium-uppercase text-text-warning"
>
{t('nodes.iteration.parallelModeUpper', { ns: 'workflow' })}
</PopoverTrigger>
<PopoverContent popupClassName="w-[180px] px-3 py-2 system-xs-regular text-text-tertiary">
<div className="font-extrabold">
{t('nodes.iteration.parallelModeEnableTitle', { ns: 'workflow' })}
</div>
{t('nodes.iteration.parallelModeEnableDesc', { ns: 'workflow' })}
</div>
)}
>
<div className="ml-1 flex items-center justify-center rounded-[5px] border border-text-warning px-[5px] py-[3px] system-2xs-medium-uppercase text-text-warning">
{t('nodes.iteration.parallelModeUpper', { ns: 'workflow' })}
</div>
</Tooltip>
</PopoverContent>
</Popover>
)
}
</div>

View File

@ -1,8 +1,8 @@
import type { NodeProps } from 'reactflow'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiHome5Fill } from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { NodeSourceHandle } from '../../node-handle'
const IterationStartNode = ({ id, data }: NodeProps) => {
@ -10,10 +10,14 @@ const IterationStartNode = ({ id, data }: NodeProps) => {
return (
<div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg shadow-xs">
<Tooltip popupContent={t('blocks.iteration-start', { ns: 'workflow' })} asChild={false}>
<div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500">
<Tooltip>
<TooltipTrigger
aria-label={t('blocks.iteration-start', { ns: 'workflow' })}
className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500 p-0"
>
<RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" />
</div>
</TooltipTrigger>
<TooltipContent>{t('blocks.iteration-start', { ns: 'workflow' })}</TooltipContent>
</Tooltip>
<NodeSourceHandle
id={id}

View File

@ -1,8 +1,8 @@
import type { NodeProps } from 'reactflow'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiHome5Fill } from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import { NodeSourceHandle } from '../../node-handle'
const LoopStartNode = ({ id, data }: NodeProps) => {
@ -10,10 +10,14 @@ const LoopStartNode = ({ id, data }: NodeProps) => {
return (
<div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg">
<Tooltip popupContent={t('blocks.loop-start', { ns: 'workflow' })} asChild={false}>
<div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500">
<Tooltip>
<TooltipTrigger
aria-label={t('blocks.loop-start', { ns: 'workflow' })}
className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500 p-0"
>
<RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" />
</div>
</TooltipTrigger>
<TooltipContent>{t('blocks.loop-start', { ns: 'workflow' })}</TooltipContent>
</Tooltip>
<NodeSourceHandle
id={id}