mirror of
https://github.com/langgenius/dify.git
synced 2026-08-15 04:59:46 +08:00
refactor(web): migrate workflow icon commands (#40649)
This commit is contained in:
parent
d6f32a0267
commit
146f402a8c
@ -3488,13 +3488,13 @@
|
||||
},
|
||||
"web/app/components/workflow/comment/mention-input.tsx": {
|
||||
"jsx_a11y/click-events-have-key-events": {
|
||||
"count": 3
|
||||
"count": 1
|
||||
},
|
||||
"jsx_a11y/no-autofocus": {
|
||||
"count": 1
|
||||
},
|
||||
"jsx_a11y/no-static-element-interactions": {
|
||||
"count": 3
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/components/workflow/context.tsx": {
|
||||
@ -4242,11 +4242,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/components/workflow/nodes/human-input/components/user-action.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/components/workflow/nodes/human-input/types.ts": {
|
||||
"erasable-syntax-only/enums": {
|
||||
"count": 2
|
||||
|
||||
@ -8,8 +8,8 @@ import type {
|
||||
ToolWithProvider,
|
||||
} from '../types'
|
||||
import type { TabType } from './types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import {
|
||||
Popover,
|
||||
PopoverClose,
|
||||
@ -130,20 +130,20 @@ function BlockSelector({
|
||||
/>
|
||||
) : (
|
||||
<PopoverTrigger
|
||||
aria-label={t(($) => $['common.addBlock'], { ns: 'workflow' })}
|
||||
disabled={disabled}
|
||||
render={
|
||||
<Button
|
||||
<IconButton
|
||||
aria-label={t(($) => $['common.addBlock'], { ns: 'workflow' })}
|
||||
variant="primary"
|
||||
size="small"
|
||||
className={cn('z-10 size-4 rounded-full p-0', triggerClassName)}
|
||||
size="xs"
|
||||
className={cn('z-10 rounded-full', triggerClassName)}
|
||||
style={triggerStyle}
|
||||
/>
|
||||
>
|
||||
<span aria-hidden className="i-custom-vender-line-general-plus-02 size-2.5" />
|
||||
</IconButton>
|
||||
}
|
||||
onClick={handleTrigger}
|
||||
>
|
||||
<span aria-hidden className="i-custom-vender-line-general-plus-02 size-2.5" />
|
||||
</PopoverTrigger>
|
||||
/>
|
||||
)
|
||||
const triggerWithTooltip = triggerTooltip ? (
|
||||
<TipPopup title={triggerTooltip}>{triggerControl}</TipPopup>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
'use client'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -7,6 +6,7 @@ import {
|
||||
DropdownMenuLinkItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@langgenius/dify-ui/dropdown-menu'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { memo } from 'react'
|
||||
@ -52,17 +52,17 @@ function OperationDropdown({ open, onOpenChange, author, name, version }: Props)
|
||||
<DropdownMenu open={open} onOpenChange={onOpenChange}>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="small"
|
||||
className="size-6 min-h-0 rounded-lg p-0 focus-visible:ring-inset data-popup-open:bg-state-base-hover"
|
||||
size="md"
|
||||
className="rounded-lg focus-visible:ring-inset data-popup-open:bg-state-base-hover"
|
||||
aria-label={t(($) => $['operation.more'], { ns: 'common' })}
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-ri-more-fill size-4 text-components-button-secondary-accent-text"
|
||||
/>
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="min-w-[176px]">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { OnNodeAdd } from '../../types'
|
||||
import type { SnippetListItem as SnippetListItemData } from '@/types/snippet'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import {
|
||||
createPreviewCardHandle,
|
||||
PreviewCard,
|
||||
@ -131,15 +131,15 @@ const Snippets = ({ searchText, onSearchTextChange, insertPayload, onInserted }:
|
||||
onChange={(event) => onSearchTextChange?.(event.target.value)}
|
||||
/>
|
||||
{!!searchText && (
|
||||
<Button
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="small"
|
||||
size="md"
|
||||
aria-label={t(($) => $['tabs.clearSnippetSearch'], { ns: 'workflow' })}
|
||||
className="size-6 min-h-0 shrink-0 p-0 focus-visible:ring-inset"
|
||||
className="shrink-0 focus-visible:ring-inset"
|
||||
onClick={() => onSearchTextChange?.('')}
|
||||
>
|
||||
<span className="i-ri-close-line size-4 text-text-tertiary" aria-hidden="true" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
<div className="mx-0 mr-0.5 h-3.5 w-px bg-divider-regular" />
|
||||
|
||||
@ -5,7 +5,7 @@ import type { UserProfile } from '@/app/components/workflow/comment/types'
|
||||
import { Avatar } from '@langgenius/dify-ui/avatar'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { RiArrowUpLine, RiAtLine, RiLoader2Line } from '@remixicon/react'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import {
|
||||
forwardRef,
|
||||
memo,
|
||||
@ -568,29 +568,35 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(
|
||||
ref={setActionContainerRef}
|
||||
className="absolute right-1 bottom-0 z-20 flex items-end gap-1"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'z-20 flex size-8 items-center justify-center rounded-lg transition-opacity',
|
||||
shouldDisableMentionButton
|
||||
? 'cursor-not-allowed opacity-40'
|
||||
: 'cursor-pointer hover:bg-state-base-hover',
|
||||
)}
|
||||
onClick={shouldDisableMentionButton ? undefined : handleMentionButtonClick}
|
||||
<IconButton
|
||||
aria-label={t(($) => $['nodes.agent.task.mention'], { ns: 'workflow' })}
|
||||
size="lg"
|
||||
disabled={shouldDisableMentionButton}
|
||||
className="z-20 transition-opacity data-disabled:opacity-40"
|
||||
onClick={handleMentionButtonClick}
|
||||
>
|
||||
<RiAtLine className="size-4 text-text-tertiary" />
|
||||
</div>
|
||||
<Button
|
||||
className="z-20 ml-2 w-8 px-0"
|
||||
<span aria-hidden className="i-ri-at-line size-4 text-text-tertiary" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label={t(($) => $['operation.send'], { ns: 'common' })}
|
||||
className="z-20 ml-2"
|
||||
size="lg"
|
||||
variant="primary"
|
||||
disabled={!value.trim() || disabled || loading}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{loading ? (
|
||||
<RiLoader2Line className="size-4 animate-spin text-components-button-primary-text" />
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-ri-loader-2-line size-4 animate-spin text-components-button-primary-text"
|
||||
/>
|
||||
) : (
|
||||
<RiArrowUpLine className="size-4 text-components-button-primary-text" />
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-ri-arrow-up-line size-4 text-components-button-primary-text"
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -599,17 +605,15 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(
|
||||
ref={setActionContainerRef}
|
||||
className="absolute inset-x-1 bottom-0 z-20 flex items-end justify-between"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'z-20 flex size-8 items-center justify-center rounded-lg transition-opacity',
|
||||
shouldDisableMentionButton
|
||||
? 'cursor-not-allowed opacity-40'
|
||||
: 'cursor-pointer hover:bg-state-base-hover',
|
||||
)}
|
||||
onClick={shouldDisableMentionButton ? undefined : handleMentionButtonClick}
|
||||
<IconButton
|
||||
aria-label={t(($) => $['nodes.agent.task.mention'], { ns: 'workflow' })}
|
||||
size="lg"
|
||||
disabled={shouldDisableMentionButton}
|
||||
className="z-20 transition-opacity data-disabled:opacity-40"
|
||||
onClick={handleMentionButtonClick}
|
||||
>
|
||||
<RiAtLine className="size-4 text-text-tertiary" />
|
||||
</div>
|
||||
<span aria-hidden className="i-ri-at-line size-4 text-text-tertiary" />
|
||||
</IconButton>
|
||||
<div ref={setActionRightRef} className="flex items-center gap-2">
|
||||
<Button variant="secondary" size="small" onClick={onCancel} disabled={loading}>
|
||||
{t(($) => $['operation.cancel'], { ns: 'common' })}
|
||||
@ -620,7 +624,9 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(
|
||||
disabled={loading || !value.trim()}
|
||||
onClick={() => handleSubmit()}
|
||||
>
|
||||
{loading && <RiLoader2Line className="size-3.5 animate-spin" />}
|
||||
{loading && (
|
||||
<span aria-hidden className="i-ri-loader-2-line size-3.5 animate-spin" />
|
||||
)}
|
||||
<span>{t(($) => $['operation.save'], { ns: 'common' })}</span>
|
||||
{!loading && <EnterKey className="size-4" />}
|
||||
</Button>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { memo } from 'react'
|
||||
import { BubbleX } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
|
||||
const ChatVariableButton = ({ disabled }: { disabled: boolean }) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
const showChatVariablePanel = useStore((s) => s.showChatVariablePanel)
|
||||
const setShowChatVariablePanel = useStore((s) => s.setShowChatVariablePanel)
|
||||
const setShowEnvPanel = useStore((s) => s.setShowEnvPanel)
|
||||
@ -21,17 +22,23 @@ const ChatVariableButton = ({ disabled }: { disabled: boolean }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
<IconButton
|
||||
aria-label={t(($) => $['chatVariable.panelTitle'], { ns: 'workflow' })}
|
||||
aria-expanded={showChatVariablePanel}
|
||||
size="lg"
|
||||
className={cn(
|
||||
'rounded-lg border border-transparent p-2',
|
||||
'border border-transparent',
|
||||
theme === 'dark' && showChatVariablePanel && 'border-black/5 bg-white/10 backdrop-blur-xs',
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
variant="ghost"
|
||||
>
|
||||
<BubbleX className="size-4 text-components-button-secondary-text" />
|
||||
</Button>
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-custom-vender-line-others-bubble-x size-4 text-components-button-secondary-text"
|
||||
/>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { memo } from 'react'
|
||||
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks/use-input-field-panel'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
|
||||
const EnvButton = ({ disabled }: { disabled: boolean }) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
const setShowChatVariablePanel = useStore((s) => s.setShowChatVariablePanel)
|
||||
const showEnvPanel = useStore((s) => s.showEnvPanel)
|
||||
const setShowEnvPanel = useStore((s) => s.setShowEnvPanel)
|
||||
@ -24,17 +25,23 @@ const EnvButton = ({ disabled }: { disabled: boolean }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
<IconButton
|
||||
aria-label={t(($) => $['env.envPanelTitle'], { ns: 'workflow' })}
|
||||
aria-expanded={showEnvPanel}
|
||||
size="lg"
|
||||
className={cn(
|
||||
'rounded-lg border border-transparent p-2',
|
||||
'border border-transparent',
|
||||
theme === 'dark' && showEnvPanel && 'border-black/5 bg-white/10 backdrop-blur-xs',
|
||||
)}
|
||||
variant="ghost"
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Env className="size-4 text-components-button-secondary-text" />
|
||||
</Button>
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-custom-vender-line-others-env size-4 text-components-button-secondary-text"
|
||||
/>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { memo } from 'react'
|
||||
import { GlobalVariable } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks/use-input-field-panel'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
|
||||
const GlobalVariableButton = ({ disabled }: { disabled: boolean }) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
const showGlobalVariablePanel = useStore((s) => s.showGlobalVariablePanel)
|
||||
const setShowGlobalVariablePanel = useStore((s) => s.setShowGlobalVariablePanel)
|
||||
const setShowEnvPanel = useStore((s) => s.setShowEnvPanel)
|
||||
@ -24,9 +25,12 @@ const GlobalVariableButton = ({ disabled }: { disabled: boolean }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
<IconButton
|
||||
aria-label={t(($) => $['globalVar.title'], { ns: 'workflow' })}
|
||||
aria-expanded={showGlobalVariablePanel}
|
||||
size="lg"
|
||||
className={cn(
|
||||
'rounded-lg border border-transparent p-2',
|
||||
'border border-transparent',
|
||||
theme === 'dark' &&
|
||||
showGlobalVariablePanel &&
|
||||
'border-black/5 bg-white/10 backdrop-blur-xs',
|
||||
@ -35,8 +39,11 @@ const GlobalVariableButton = ({ disabled }: { disabled: boolean }) => {
|
||||
onClick={handleClick}
|
||||
variant="ghost"
|
||||
>
|
||||
<GlobalVariable className="size-4 text-components-button-secondary-text" />
|
||||
</Button>
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-custom-vender-line-others-global-variable size-4 text-components-button-secondary-text"
|
||||
/>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ViewWorkflowHistory from '@/app/components/workflow/header/view-workflow-history'
|
||||
@ -29,38 +28,30 @@ function UndoRedo({ handleUndo, handleRedo }: UndoRedoProps) {
|
||||
return (
|
||||
<div className="flex items-center space-x-0.5 rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-lg backdrop-blur-[5px]">
|
||||
<TipPopup title={t(($) => $['common.undo'], { ns: 'workflow' })!} shortcut="workflow.undo">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
<IconButton
|
||||
size="lg"
|
||||
className="rounded-md"
|
||||
aria-label={t(($) => $['common.undo'], { ns: 'workflow' })!}
|
||||
data-tooltip-id="workflow.undo"
|
||||
disabled={nodesReadOnly || buttonsDisabled.undo}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'size-8 p-0 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
onClick={handleUndo}
|
||||
>
|
||||
<span aria-hidden className="i-ri-arrow-go-back-line size-4" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
</TipPopup>
|
||||
<TipPopup title={t(($) => $['common.redo'], { ns: 'workflow' })!} shortcut="workflow.redo">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
<IconButton
|
||||
size="lg"
|
||||
className="rounded-md"
|
||||
aria-label={t(($) => $['common.redo'], { ns: 'workflow' })!}
|
||||
data-tooltip-id="workflow.redo"
|
||||
disabled={nodesReadOnly || buttonsDisabled.redo}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'size-8 p-0 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
onClick={handleRedo}
|
||||
>
|
||||
<span aria-hidden className="i-ri-arrow-go-forward-fill size-4" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
</TipPopup>
|
||||
<Divider type="vertical" className="mx-0.5 h-3.5" />
|
||||
<ViewWorkflowHistory />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
import { useHotkey } from '@tanstack/react-hotkeys'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -42,10 +42,12 @@ export function VersionHistoryButton({ onClick }: VersionHistoryButtonProps) {
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={
|
||||
<Button
|
||||
<IconButton
|
||||
aria-label={label}
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
className={cn(
|
||||
'rounded-lg p-2 inset-ring-1 inset-ring-transparent',
|
||||
'inset-ring-1 inset-ring-transparent',
|
||||
theme === 'dark' && 'bg-white/10 inset-ring-black/5 backdrop-blur-xs',
|
||||
)}
|
||||
onClick={onClick}
|
||||
@ -54,7 +56,7 @@ export function VersionHistoryButton({ onClick }: VersionHistoryButtonProps) {
|
||||
aria-hidden
|
||||
className="i-ri-history-line size-4 text-components-button-secondary-text"
|
||||
/>
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
<TooltipContent className="rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg p-1.5 shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { WorkflowHistoryState } from '../store/workflow/history-slice'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Popover, PopoverClose, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import { memo, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -156,26 +156,22 @@ const ViewWorkflowHistory = () => {
|
||||
)
|
||||
}
|
||||
render={
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
<IconButton
|
||||
size="lg"
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
aria-label={t(($) => $['changeHistory.title'], { ns: 'workflow' })}
|
||||
className={cn(
|
||||
'size-8 p-0 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
className="rounded-md"
|
||||
onClick={() => {
|
||||
if (nodesReadOnly) return
|
||||
setCurrentLogItem()
|
||||
setShowMessageLogModal(false)
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<span aria-hidden className="i-ri-history-line size-4 shrink-0" />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<span aria-hidden className="i-ri-history-line size-4 shrink-0" />
|
||||
</PopoverTrigger>
|
||||
/>
|
||||
</TipPopup>
|
||||
<PopoverContent
|
||||
placement="bottom-end"
|
||||
@ -188,14 +184,14 @@ const ViewWorkflowHistory = () => {
|
||||
</div>
|
||||
<PopoverClose
|
||||
render={
|
||||
<Button
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="small"
|
||||
size="md"
|
||||
aria-label={t(($) => $['operation.close'], { ns: 'common' })}
|
||||
className="size-6 shrink-0 p-0 text-text-secondary hover:bg-state-base-hover"
|
||||
className="shrink-0 text-text-secondary hover:bg-state-base-hover"
|
||||
>
|
||||
<span aria-hidden className="i-ri-close-line size-4 text-text-secondary" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
onClick={() => {
|
||||
setCurrentLogItem()
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import ToggleExpandBtn from '../toggle-expand-btn'
|
||||
|
||||
describe('ToggleExpandBtn', () => {
|
||||
it('exposes the localized next action and toggles expansion', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onExpandChange = vi.fn()
|
||||
const { rerender } = render(
|
||||
<ToggleExpandBtn isExpand={false} onExpandChange={onExpandChange} />,
|
||||
)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'share.chat.expand' }))
|
||||
expect(onExpandChange).toHaveBeenCalledWith(true)
|
||||
|
||||
rerender(<ToggleExpandBtn isExpand onExpandChange={onExpandChange} />)
|
||||
await user.click(screen.getByRole('button', { name: 'share.chat.collapse' }))
|
||||
expect(onExpandChange).toHaveBeenLastCalledWith(false)
|
||||
})
|
||||
})
|
||||
@ -5,6 +5,7 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from '@langgenius/dify-ui/dropdown-menu'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { intersection } from 'es-toolkit/array'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -71,12 +72,14 @@ const Operator = ({ open, onOpenChange, data, nodeId, sourceHandle }: OperatorPr
|
||||
<DropdownMenu open={open} onOpenChange={onOpenChange}>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
className="size-6 p-0"
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
size="md"
|
||||
className="rounded-lg"
|
||||
aria-label={t(($) => $['common.moreActions'], { ns: 'workflow' })}
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-fill size-4" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent
|
||||
|
||||
@ -4,6 +4,7 @@ import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type Props = Readonly<{
|
||||
isExpand: boolean
|
||||
@ -11,12 +12,16 @@ type Props = Readonly<{
|
||||
}>
|
||||
|
||||
const ExpandBtn: FC<Props> = ({ isExpand, onExpandChange }) => {
|
||||
const { t } = useTranslation()
|
||||
const handleToggle = useCallback(() => {
|
||||
onExpandChange(!isExpand)
|
||||
}, [isExpand, onExpandChange])
|
||||
|
||||
return (
|
||||
<IconButton aria-label={isExpand ? 'Collapse' : 'Expand'} onClick={handleToggle}>
|
||||
<IconButton
|
||||
aria-label={t(($) => $[isExpand ? 'chat.collapse' : 'chat.expand'], { ns: 'share' })}
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
|
||||
@ -10,9 +10,8 @@ vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => mockUseTranslation(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/input', () => ({
|
||||
__esModule: true,
|
||||
default: (props: {
|
||||
vi.mock('@langgenius/dify-ui/input', () => ({
|
||||
Input: (props: {
|
||||
value: string
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import type { FC } from 'react'
|
||||
import type { UserAction } from '../types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { RiDeleteBinLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '@/app/components/base/input'
|
||||
import ButtonStyleDropdown from './button-style-dropdown'
|
||||
|
||||
const i18nPrefix = 'nodes.humanInput'
|
||||
@ -75,7 +74,7 @@ const UserActionItem: FC<UserActionItemProps> = ({ data, onChange, onDelete, rea
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="shrink-0">
|
||||
<Input
|
||||
wrapperClassName="w-[120px]"
|
||||
className="w-[120px]"
|
||||
value={data.id}
|
||||
placeholder={t(($) => $[`${i18nPrefix}.userActions.actionNamePlaceholder`], {
|
||||
ns: 'workflow',
|
||||
@ -101,14 +100,14 @@ const UserActionItem: FC<UserActionItemProps> = ({ data, onChange, onDelete, rea
|
||||
readonly={readonly}
|
||||
/>
|
||||
{!readonly && (
|
||||
<Button
|
||||
<IconButton
|
||||
aria-label={t(($) => $['operation.delete'], { ns: 'common' })}
|
||||
className="px-2"
|
||||
size="lg"
|
||||
variant="tertiary"
|
||||
onClick={() => onDelete(data.id)}
|
||||
>
|
||||
<RiDeleteBinLine className="size-4" />
|
||||
</Button>
|
||||
<span aria-hidden className="i-ri-delete-bin-line size-4" />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { NodeDefault } from '../../types'
|
||||
import { screen } from '@testing-library/react'
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { FlowType } from '@/types/common'
|
||||
import { createNode } from '../../__tests__/fixtures'
|
||||
@ -128,17 +128,20 @@ describe('AddBlock', () => {
|
||||
mockFlowType = FlowType.appFlow
|
||||
})
|
||||
|
||||
it('opens the real selector and derives trigger styling from its open state', async () => {
|
||||
it('opens the real selector and exposes its open state', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderWithReactFlow()
|
||||
|
||||
const trigger = screen.getByRole('button', { name: 'workflow.common.addBlock' })
|
||||
expect(trigger).not.toHaveAttribute('data-popup-open')
|
||||
expect(trigger).not.toHaveAttribute('data-block-selector-open')
|
||||
|
||||
await user.hover(trigger)
|
||||
await waitFor(() => expect(trigger).toHaveAttribute('data-popup-open', ''))
|
||||
expect(trigger).not.toHaveAttribute('data-block-selector-open')
|
||||
|
||||
await user.click(trigger)
|
||||
|
||||
expect(trigger).toHaveAttribute('data-popup-open')
|
||||
expect(trigger).toHaveClass('bg-state-accent-active')
|
||||
expect(trigger).toHaveAttribute('data-block-selector-open', '')
|
||||
expect(screen.getByRole('dialog', { name: 'workflow.common.addBlock' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Answer' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -85,18 +85,20 @@ describe('Control', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// Rendering and visual states for control buttons.
|
||||
// Rendering and semantic states for control buttons.
|
||||
describe('Rendering', () => {
|
||||
it('should render the child action groups and highlight the active pointer mode', () => {
|
||||
render(<Control />)
|
||||
|
||||
expect(screen.getByTestId('add-block')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('more-actions')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('workflow.common.pointerMode').firstElementChild).toHaveClass(
|
||||
'bg-state-accent-active',
|
||||
expect(screen.getByRole('button', { name: 'workflow.common.pointerMode' })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
)
|
||||
expect(screen.getByTestId('workflow.common.handMode').firstElementChild).not.toHaveClass(
|
||||
'bg-state-accent-active',
|
||||
expect(screen.getByRole('button', { name: 'workflow.common.handMode' })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'false',
|
||||
)
|
||||
})
|
||||
|
||||
@ -107,8 +109,9 @@ describe('Control', () => {
|
||||
|
||||
render(<Control />)
|
||||
|
||||
expect(screen.getByTestId('workflow.common.handMode').firstElementChild).toHaveClass(
|
||||
'bg-state-accent-active',
|
||||
expect(screen.getByRole('button', { name: 'workflow.common.handMode' })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import type { BlockSelectorProps } from '@/app/components/workflow/block-selector'
|
||||
import type { Node, OnSelectBlock } from '@/app/components/workflow/types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { memo, useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
@ -28,7 +26,6 @@ type AddBlockProps = {
|
||||
onClose?: () => void
|
||||
isolateKeyboardEvents?: boolean
|
||||
}
|
||||
type BlockSelectorTriggerRender = Exclude<NonNullable<BlockSelectorProps['trigger']>, ReactElement>
|
||||
const AddBlock = ({
|
||||
renderTrigger,
|
||||
sideOffset,
|
||||
@ -85,27 +82,17 @@ const AddBlock = ({
|
||||
[store, workflowStore, nodesMetaDataMap],
|
||||
)
|
||||
|
||||
const renderTriggerElement = useCallback<BlockSelectorTriggerRender>(
|
||||
(props, state) => {
|
||||
return (
|
||||
<Button
|
||||
{...props}
|
||||
variant="ghost"
|
||||
size="small"
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'size-8 p-0 text-text-tertiary hover:text-text-secondary',
|
||||
'data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
state.open && 'bg-state-accent-active text-text-accent',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
<span aria-hidden className="i-ri-add-circle-fill size-4" />
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
[nodesReadOnly],
|
||||
const renderTriggerElement = (
|
||||
<IconButton
|
||||
aria-label={t(($) => $['common.addBlock'], { ns: 'workflow' })}
|
||||
size="lg"
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
data-block-selector-open={open ? '' : undefined}
|
||||
className="rounded-md data-block-selector-open:bg-state-accent-active data-block-selector-open:text-text-accent"
|
||||
>
|
||||
<span aria-hidden className="i-ri-add-circle-fill size-4" />
|
||||
</IconButton>
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { MouseEvent } from 'react'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Toggle } from '@langgenius/dify-ui/toggle'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Divider from '../../base/divider'
|
||||
@ -14,6 +14,9 @@ import { useOperator } from './hooks'
|
||||
import MoreActions from './more-actions'
|
||||
import TipPopup from './tip-popup'
|
||||
|
||||
const pressedModeClassName =
|
||||
'data-pressed:bg-state-accent-active data-pressed:text-text-accent data-pressed:hover:bg-state-base-hover data-pressed:hover:text-text-secondary data-disabled:data-pressed:text-text-disabled data-disabled:data-pressed:hover:bg-transparent data-disabled:data-pressed:hover:text-text-disabled'
|
||||
|
||||
const Control = () => {
|
||||
const { t } = useTranslation()
|
||||
const controlMode = useStore((s) => s.controlMode)
|
||||
@ -39,88 +42,84 @@ const Control = () => {
|
||||
<div className="pointer-events-auto flex flex-col items-center rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 text-text-tertiary shadow-lg">
|
||||
<AddBlock />
|
||||
<TipPopup title={t(($) => $['nodes.note.addNote'], { ns: 'workflow' })}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={t(($) => $['nodes.note.addNote'], { ns: 'workflow' })}
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'ml-px size-8 p-0 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
className="ml-px rounded-md"
|
||||
onClick={addNote}
|
||||
>
|
||||
<span aria-hidden className="i-ri-sticky-note-add-line size-4" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
</TipPopup>
|
||||
<Divider className="my-1 w-3.5" />
|
||||
<TipPopup
|
||||
title={t(($) => $['common.pointerMode'], { ns: 'workflow' })}
|
||||
shortcut="workflow.pointer-mode"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
aria-label={t(($) => $['common.pointerMode'], { ns: 'workflow' })}
|
||||
<Toggle
|
||||
pressed={controlMode === ControlMode.Pointer}
|
||||
onPressedChange={handleModePointer}
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'mr-px size-8 p-0 text-text-tertiary',
|
||||
controlMode === ControlMode.Pointer
|
||||
? 'bg-state-accent-active text-text-accent'
|
||||
: 'hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
onClick={handleModePointer}
|
||||
>
|
||||
<span aria-hidden className="i-ri-cursor-line size-4" />
|
||||
</Button>
|
||||
className={pressedModeClassName}
|
||||
render={
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={t(($) => $['common.pointerMode'], { ns: 'workflow' })}
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
className="mr-px rounded-md"
|
||||
>
|
||||
<span aria-hidden className="i-ri-cursor-line size-4" />
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
</TipPopup>
|
||||
<TipPopup
|
||||
title={t(($) => $['common.handMode'], { ns: 'workflow' })}
|
||||
shortcut="workflow.hand-mode"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
aria-label={t(($) => $['common.handMode'], { ns: 'workflow' })}
|
||||
<Toggle
|
||||
pressed={controlMode === ControlMode.Hand}
|
||||
onPressedChange={handleModeHand}
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'size-8 p-0 text-text-tertiary',
|
||||
controlMode === ControlMode.Hand
|
||||
? 'bg-state-accent-active text-text-accent'
|
||||
: 'hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
onClick={handleModeHand}
|
||||
>
|
||||
<span aria-hidden className="i-ri-hand size-4" />
|
||||
</Button>
|
||||
className={pressedModeClassName}
|
||||
render={
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={t(($) => $['common.handMode'], { ns: 'workflow' })}
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
className="rounded-md"
|
||||
>
|
||||
<span aria-hidden className="i-ri-hand size-4" />
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
</TipPopup>
|
||||
{isCommentModeAvailable && (
|
||||
<TipPopup
|
||||
title={t(($) => $['common.commentMode'], { ns: 'workflow' })}
|
||||
shortcut="workflow.comment-mode"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
aria-label={t(($) => $['common.commentMode'], { ns: 'workflow' })}
|
||||
<Toggle
|
||||
pressed={controlMode === ControlMode.Comment}
|
||||
onPressedChange={handleModeComment}
|
||||
disabled={!canUseCommentMode}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'ml-px size-8 p-0 text-text-tertiary',
|
||||
controlMode === ControlMode.Comment
|
||||
? 'bg-state-accent-active text-text-accent'
|
||||
: 'hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
onClick={handleModeComment}
|
||||
>
|
||||
<span aria-hidden className="i-custom-public-other-comment size-4" />
|
||||
</Button>
|
||||
className={pressedModeClassName}
|
||||
render={
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={t(($) => $['common.commentMode'], { ns: 'workflow' })}
|
||||
disabled={!canUseCommentMode}
|
||||
focusableWhenDisabled
|
||||
className="ml-px rounded-md"
|
||||
>
|
||||
<span aria-hidden className="i-custom-public-other-comment size-4" />
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
</TipPopup>
|
||||
)}
|
||||
<Divider className="my-1 w-3.5" />
|
||||
@ -128,20 +127,16 @@ const Control = () => {
|
||||
title={t(($) => $['panel.organizeBlocks'], { ns: 'workflow' })}
|
||||
shortcut="workflow.organize"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={t(($) => $['panel.organizeBlocks'], { ns: 'workflow' })}
|
||||
disabled={nodesReadOnly}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'size-8 p-0 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
className="rounded-md"
|
||||
onClick={handleLayout}
|
||||
>
|
||||
<span aria-hidden className="i-ri-function-add-line size-4" />
|
||||
</Button>
|
||||
</IconButton>
|
||||
</TipPopup>
|
||||
<MoreActions />
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -7,6 +5,7 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@langgenius/dify-ui/dropdown-menu'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { toJpeg, toPng, toSvg } from 'html-to-image'
|
||||
import { memo, useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -158,21 +157,17 @@ function MoreActions() {
|
||||
<TipPopup title={t(($) => $['common.moreActions'], { ns: 'workflow' })}>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
<IconButton
|
||||
size="lg"
|
||||
aria-label={t(($) => $['common.moreActions'], { ns: 'workflow' })}
|
||||
disabled={isReadOnly}
|
||||
focusableWhenDisabled
|
||||
className={cn(
|
||||
'size-8 p-0 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'data-disabled:cursor-not-allowed data-disabled:text-text-disabled data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled',
|
||||
)}
|
||||
/>
|
||||
className="rounded-md"
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-fill size-4" />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-fill size-4" />
|
||||
</DropdownMenuTrigger>
|
||||
/>
|
||||
</TipPopup>
|
||||
<DropdownMenuContent placement="right-end" popupClassName="min-w-[180px]">
|
||||
<div className="flex items-center gap-2 px-2 py-1 text-xs font-medium text-text-tertiary">
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import type { FC } from 'react'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@langgenius/dify-ui/dropdown-menu'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { VersionHistoryContextMenuOptions } from '../../../types'
|
||||
@ -31,16 +31,16 @@ const ActionMenu: FC<ActionMenuProps> = (props: ActionMenuProps) => {
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
size="small"
|
||||
className="px-1"
|
||||
<IconButton
|
||||
size="md"
|
||||
variant="secondary"
|
||||
aria-label={t(($) => $['operation.more'], { ns: 'common' })}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-fill size-4" />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-fill size-4" />
|
||||
</DropdownMenuTrigger>
|
||||
/>
|
||||
<DropdownMenuContent
|
||||
placement="bottom-end"
|
||||
sideOffset={4}
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import type { AgentLogItemWithChildren } from '@/types/workflow'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@langgenius/dify-ui/dropdown-menu'
|
||||
import { RiMoreLine } from '@remixicon/react'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@ -22,15 +21,16 @@ const AgentLogNavMore = ({ options, onShowAgentOrToolLog }: AgentLogNavMoreProps
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
<IconButton
|
||||
aria-label={t(($) => $['operation.more'], { ns: 'common' })}
|
||||
className="size-6"
|
||||
size="md"
|
||||
variant="ghost-accent"
|
||||
/>
|
||||
className="rounded-lg"
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-line size-4" />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<RiMoreLine className="size-4" />
|
||||
</DropdownMenuTrigger>
|
||||
/>
|
||||
<DropdownMenuContent
|
||||
placement="bottom-start"
|
||||
sideOffset={2}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user