mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
feat: add useFloatingRight hook and integrate it into InputFieldEditorPanel and PreviewPanel for dynamic positioning
This commit is contained in:
@ -1,11 +1,13 @@
|
|||||||
import { RiCloseLine } from '@remixicon/react'
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
import InputFieldForm from './form'
|
import InputFieldForm from './form'
|
||||||
import { convertFormDataToINputField, convertToInputFieldFormData } from './utils'
|
import { convertFormDataToINputField, convertToInputFieldFormData } from './utils'
|
||||||
import { useCallback } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { InputVar } from '@/models/pipeline'
|
import type { InputVar } from '@/models/pipeline'
|
||||||
import type { FormData } from './form/types'
|
import type { FormData } from './form/types'
|
||||||
import type { MoreInfo } from '@/app/components/workflow/types'
|
import type { MoreInfo } from '@/app/components/workflow/types'
|
||||||
|
import { useFloatingRight } from '../hooks'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
export type InputFieldEditorProps = {
|
export type InputFieldEditorProps = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
@ -19,7 +21,12 @@ const InputFieldEditorPanel = ({
|
|||||||
initialData,
|
initialData,
|
||||||
}: InputFieldEditorProps) => {
|
}: InputFieldEditorProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const formData = convertToInputFieldFormData(initialData)
|
|
||||||
|
const { floatingRight, floatingRightWidth } = useFloatingRight(400)
|
||||||
|
|
||||||
|
const formData = useMemo(() => {
|
||||||
|
return convertToInputFieldFormData(initialData)
|
||||||
|
}, [initialData])
|
||||||
|
|
||||||
const handleSubmit = useCallback((value: FormData, moreInfo?: MoreInfo) => {
|
const handleSubmit = useCallback((value: FormData, moreInfo?: MoreInfo) => {
|
||||||
const inputFieldData = convertFormDataToINputField(value)
|
const inputFieldData = convertFormDataToINputField(value)
|
||||||
@ -27,7 +34,16 @@ const InputFieldEditorPanel = ({
|
|||||||
}, [onSubmit])
|
}, [onSubmit])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative mr-1 flex h-fit max-h-full w-[400px] flex-col overflow-y-auto rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9'>
|
<div
|
||||||
|
className={cn(
|
||||||
|
'relative mr-1 flex h-fit max-h-full w-[400px] flex-col overflow-y-auto rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9',
|
||||||
|
'transition-all duration-300 ease-in-out',
|
||||||
|
floatingRight && 'absolute right-0 z-[100]',
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
width: `${floatingRightWidth}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className='system-xl-semibold flex items-center pb-1 pl-4 pr-11 pt-3.5 text-text-primary'>
|
<div className='system-xl-semibold flex items-center pb-1 pl-4 pr-11 pt-3.5 text-text-primary'>
|
||||||
{initialData ? t('datasetPipeline.inputFieldPanel.editInputField') : t('datasetPipeline.inputFieldPanel.addInputField')}
|
{initialData ? t('datasetPipeline.inputFieldPanel.editInputField') : t('datasetPipeline.inputFieldPanel.addInputField')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
|
import { useStore as useReactflow } from 'reactflow'
|
||||||
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
|
|
||||||
|
export const useFloatingRight = (targetElementWidth: number) => {
|
||||||
|
const [floatingRight, setFloatingRight] = useState(false)
|
||||||
|
const nodePanelWidth = useStore(state => state.nodePanelWidth)
|
||||||
|
const workflowCanvasWidth = useStore(state => state.workflowCanvasWidth)
|
||||||
|
const otherPanelWidth = useStore(state => state.otherPanelWidth)
|
||||||
|
|
||||||
|
const selectedNodeId = useReactflow(useShallow((s) => {
|
||||||
|
const nodes = s.getNodes()
|
||||||
|
const currentNode = nodes.find(node => node.data.selected)
|
||||||
|
|
||||||
|
if (currentNode)
|
||||||
|
return currentNode.id
|
||||||
|
}))
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof workflowCanvasWidth === 'number') {
|
||||||
|
const inputFieldPanelWidth = 400
|
||||||
|
const marginRight = 4
|
||||||
|
const leftWidth = workflowCanvasWidth - (selectedNodeId ? nodePanelWidth : 0) - otherPanelWidth - inputFieldPanelWidth - marginRight
|
||||||
|
setFloatingRight(leftWidth < targetElementWidth + marginRight)
|
||||||
|
}
|
||||||
|
}, [workflowCanvasWidth, nodePanelWidth, otherPanelWidth, selectedNodeId, targetElementWidth])
|
||||||
|
|
||||||
|
const floatingRightWidth = useMemo(() => {
|
||||||
|
if (!floatingRight) return targetElementWidth
|
||||||
|
const width = Math.min(targetElementWidth, (selectedNodeId ? nodePanelWidth : 0) + otherPanelWidth)
|
||||||
|
return width
|
||||||
|
}, [floatingRight, selectedNodeId, nodePanelWidth, otherPanelWidth, targetElementWidth])
|
||||||
|
|
||||||
|
return {
|
||||||
|
floatingRight,
|
||||||
|
floatingRightWidth,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,18 +7,31 @@ import Divider from '@/app/components/base/divider'
|
|||||||
import ProcessDocuments from './process-documents'
|
import ProcessDocuments from './process-documents'
|
||||||
import type { Datasource } from '../../test-run/types'
|
import type { Datasource } from '../../test-run/types'
|
||||||
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks'
|
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import { useFloatingRight } from '../hooks'
|
||||||
|
|
||||||
const PreviewPanel = () => {
|
const PreviewPanel = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [datasource, setDatasource] = useState<Datasource>()
|
const [datasource, setDatasource] = useState<Datasource>()
|
||||||
const { toggleInputFieldPreviewPanel } = useInputFieldPanel()
|
const { toggleInputFieldPreviewPanel } = useInputFieldPanel()
|
||||||
|
|
||||||
|
const { floatingRight, floatingRightWidth } = useFloatingRight(480)
|
||||||
|
|
||||||
const handleClosePreviewPanel = useCallback(() => {
|
const handleClosePreviewPanel = useCallback(() => {
|
||||||
toggleInputFieldPreviewPanel()
|
toggleInputFieldPreviewPanel()
|
||||||
}, [toggleInputFieldPreviewPanel])
|
}, [toggleInputFieldPreviewPanel])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mr-1 flex h-full w-[480px] flex-col overflow-y-auto rounded-2xl border-y-[0.5px] border-l-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5'>
|
<div
|
||||||
|
className={cn(
|
||||||
|
'mr-1 flex h-full flex-col overflow-y-auto rounded-2xl border-y-[0.5px] border-l-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5',
|
||||||
|
'transition-all duration-300 ease-in-out',
|
||||||
|
floatingRight && 'absolute right-0 z-[100]',
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
width: `${floatingRightWidth}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className='flex items-center gap-x-2 px-4 pt-1'>
|
<div className='flex items-center gap-x-2 px-4 pt-1'>
|
||||||
<div className='grow py-1'>
|
<div className='grow py-1'>
|
||||||
<Badge className='border-text-accent-secondary bg-components-badge-bg-dimm text-text-accent-secondary'>
|
<Badge className='border-text-accent-secondary bg-components-badge-bg-dimm text-text-accent-secondary'>
|
||||||
|
|||||||
Reference in New Issue
Block a user