mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
features
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore } from './store'
|
||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import {
|
||||
@ -7,12 +8,13 @@ import {
|
||||
} from '@/app/components/base/features'
|
||||
|
||||
const Features = () => {
|
||||
const { t } = useTranslation()
|
||||
const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel)
|
||||
|
||||
return (
|
||||
<div className='fixed top-16 left-2 bottom-2 w-[600px] rounded-2xl border-[0.5px] border-gray-200 bg-white shadow-xl z-10'>
|
||||
<div className='flex items-center justify-between px-4 pt-3'>
|
||||
Features
|
||||
{t('workflow.common.features')}
|
||||
<div className='flex items-center'>
|
||||
<FeaturesChoose />
|
||||
<div className='mx-3 w-[1px] h-[14px] bg-gray-200'></div>
|
||||
|
||||
34
web/app/components/workflow/header/editing-title.tsx
Normal file
34
web/app/components/workflow/header/editing-title.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { memo } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Edit03 } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
|
||||
const EditingTitle = () => {
|
||||
const { t } = useTranslation()
|
||||
const draftUpdatedAt = useStore(state => state.draftUpdatedAt)
|
||||
const publishedAt = useStore(state => state.publishedAt)
|
||||
|
||||
return (
|
||||
<div className='flex items-center h-[18px] text-xs text-gray-500'>
|
||||
<Edit03 className='mr-1 w-3 h-3 text-gray-400' />
|
||||
{t('workflow.common.editing')}
|
||||
{
|
||||
draftUpdatedAt && (
|
||||
<>
|
||||
<span className='flex items-center mx-1'>·</span>
|
||||
{t('workflow.common.autoSaved')} {dayjs(draftUpdatedAt).format('HH:mm:ss')}
|
||||
</>
|
||||
)
|
||||
}
|
||||
<span className='flex items-center mx-1'>·</span>
|
||||
{
|
||||
publishedAt
|
||||
? `${t('workflow.common.published')} ${dayjs(publishedAt).fromNow()}`
|
||||
: t('workflow.common.unpublished')
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(EditingTitle)
|
||||
@ -3,26 +3,27 @@ import {
|
||||
memo,
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore } from '../store'
|
||||
import RunAndHistory from './run-and-history'
|
||||
import EditingTitle from './editing-title'
|
||||
import RunningTitle from './running-title'
|
||||
import Publish from './publish'
|
||||
import { Edit03 } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { Grid01 } from '@/app/components/base/icons/src/vender/line/layout'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { Mode } from '@/app/components/workflow/types'
|
||||
|
||||
const Header: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel)
|
||||
const runStaus = useStore(state => state.runStaus)
|
||||
const setRunStaus = useStore(state => state.setRunStaus)
|
||||
const draftUpdatedAt = useStore(state => state.draftUpdatedAt)
|
||||
const mode = useStore(state => state.mode)
|
||||
const runTaskId = useStore(state => state.runTaskId)
|
||||
|
||||
const handleShowFeatures = useCallback(() => {
|
||||
setShowFeaturesPanel(true)
|
||||
}, [setShowFeaturesPanel])
|
||||
useStore.setState({ showFeaturesPanel: true })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -33,35 +34,25 @@ const Header: FC = () => {
|
||||
>
|
||||
<div>
|
||||
<div className='text-xs font-medium text-gray-700'>{appDetail?.name}</div>
|
||||
<div className='flex items-center'>
|
||||
<div className='flex items-center text-xs text-gray-500'>
|
||||
<Edit03 className='mr-1 w-3 h-3 text-gray-400' />
|
||||
Editing
|
||||
{
|
||||
draftUpdatedAt && (
|
||||
<>
|
||||
<span className='flex items-center mx-1'>·</span>
|
||||
<span>
|
||||
Auto-Saved {dayjs(draftUpdatedAt).format('HH:mm:ss')}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
mode === Mode.Editing && !runTaskId && <EditingTitle />
|
||||
}
|
||||
{
|
||||
(mode === Mode.Running || runTaskId) && <RunningTitle />
|
||||
}
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
runStaus && (
|
||||
(mode === Mode.Running || runTaskId) && (
|
||||
<Button
|
||||
className={`
|
||||
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
|
||||
border-[0.5px] border-gray-200 shadow-xs
|
||||
`}
|
||||
onClick={() => setRunStaus('')}
|
||||
onClick={() => useStore.setState({ mode: Mode.Editing, runTaskId: '' })}
|
||||
>
|
||||
<ArrowNarrowLeft className='mr-1 w-4 h-4' />
|
||||
Go back to editor
|
||||
{t('workflow.common.goBackToEdit')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@ -77,7 +68,7 @@ const Header: FC = () => {
|
||||
onClick={handleShowFeatures}
|
||||
>
|
||||
<Grid01 className='mr-1 w-4 h-4 text-gray-500' />
|
||||
Features
|
||||
{t('workflow.common.features')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
@ -7,6 +8,7 @@ import {
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
|
||||
const Publish = () => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
@ -24,35 +26,35 @@ const Publish = () => {
|
||||
type='primary'
|
||||
className='px-3 py-0 h-8 text-[13px] font-medium'
|
||||
>
|
||||
publish
|
||||
{t('workflow.common.publish')}
|
||||
</Button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[11]'>
|
||||
<div className='w-[320px] bg-white rounded-2xl border-[0.5px] border-gray-200 shadow-xl'>
|
||||
<div className='p-4 pt-3'>
|
||||
<div className='flex items-center h-6 text-xs font-medium text-gray-500'>
|
||||
Current Draft
|
||||
{t('workflow.common.currentDraft').toLocaleUpperCase()}
|
||||
</div>
|
||||
<div className='flex items-center h-[18px] text-[13px] font-medium text-gray-700'>
|
||||
Auto-Saved 3 min ago · Evan
|
||||
{t('workflow.common.autoSaved')} 3 min ago · Evan
|
||||
</div>
|
||||
<Button
|
||||
type='primary'
|
||||
className='mt-3 px-3 py-0 w-full h-8 border-[0.5px] border-primary-700 rounded-lg text-[13px] font-medium'
|
||||
>
|
||||
Publish
|
||||
{t('workflow.common.publish')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className='p-4 pt-3 border-t-[0.5px] border-t-black/5'>
|
||||
<div className='flex items-center h-6 text-xs font-medium text-gray-500'>
|
||||
Latest Published
|
||||
{t('workflow.common.latestPublished').toLocaleUpperCase()}
|
||||
</div>
|
||||
<div className='flex justify-between'>
|
||||
<div className='flex items-center mt-[3px] mb-[3px] leading-[18px] text-[13px] font-medium text-gray-700'>
|
||||
Auto-Saved 3 min ago · Evan
|
||||
{t('workflow.common.autoSaved')} 3 min ago · Evan
|
||||
</div>
|
||||
<Button className='ml-2 px-2 py-0 h-6 shadow-xs rounded-md text-xs font-medium text-gray-700 border-[0.5px] border-gray-200'>
|
||||
Restore
|
||||
{t('workflow.common.restore')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
import type { FC } from 'react'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore } from '../store'
|
||||
import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
||||
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
|
||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { Mode } from '@/app/components/workflow/types'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
|
||||
const RunAndHistory: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const mode = useStore(state => state.mode)
|
||||
const showRunHistory = useStore(state => state.showRunHistory)
|
||||
const setShowRunHistory = useStore(state => state.setShowRunHistory)
|
||||
const runStaus = useStore(state => state.runStaus)
|
||||
const setRunStaus = useStore(state => state.setRunStaus)
|
||||
|
||||
return (
|
||||
<div className='flex items-center px-0.5 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'>
|
||||
@ -18,38 +21,51 @@ const RunAndHistory: FC = () => {
|
||||
className={`
|
||||
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
|
||||
hover:bg-primary-50 cursor-pointer
|
||||
${runStaus === 'running' && 'bg-primary-50 !cursor-not-allowed'}
|
||||
${mode === 'running' && 'bg-primary-50 !cursor-not-allowed'}
|
||||
${mode === 'running' && appDetail?.mode !== 'workflow' && 'opacity-50'}
|
||||
`}
|
||||
onClick={() => runStaus !== 'running' && setRunStaus('running')}
|
||||
onClick={() => mode !== 'running' && useStore.setState({ mode: Mode.Running })}
|
||||
>
|
||||
{
|
||||
runStaus === 'running'
|
||||
mode === 'running'
|
||||
? (
|
||||
<>
|
||||
<Loading02 className='mr-1 w-4 h-4 animate-spin' />
|
||||
Running
|
||||
{
|
||||
appDetail?.mode === 'workflow' && (
|
||||
<Loading02 className='mr-1 w-4 h-4 animate-spin' />
|
||||
)
|
||||
}
|
||||
{
|
||||
appDetail?.mode === 'workflow'
|
||||
? t('workflow.common.running')
|
||||
: t('workflow.common.inPreview')
|
||||
}
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<Play className='mr-1 w-4 h-4' />
|
||||
Run
|
||||
{
|
||||
appDetail?.mode === 'workflow'
|
||||
? t('workflow.common.run')
|
||||
: t('workflow.common.preview')
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='mx-0.5 w-[0.5px] h-8 bg-gray-200'></div>
|
||||
<TooltipPlus
|
||||
popupContent='View run history'
|
||||
popupContent={t('workflow.common.viewRunHistory')}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/5 cursor-pointer
|
||||
${showRunHistory && 'bg-black/5'}
|
||||
${showRunHistory && 'bg-primary-50'}
|
||||
`}
|
||||
onClick={() => setShowRunHistory(true)}
|
||||
onClick={() => useStore.setState({ showRunHistory: true })}
|
||||
>
|
||||
<ClockPlay className='w-4 h-4 text-gray-500' />
|
||||
<ClockPlay className={`w-4 h-4 ${showRunHistory ? 'text-primary-600' : 'text-gray-500'}`} />
|
||||
</div>
|
||||
</TooltipPlus>
|
||||
</div>
|
||||
|
||||
24
web/app/components/workflow/header/running-title.tsx
Normal file
24
web/app/components/workflow/header/running-title.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { Play } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
|
||||
|
||||
const RunningTitle = () => {
|
||||
const { t } = useTranslation()
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
|
||||
return (
|
||||
<div className='flex items-center h-[18px] text-xs text-primary-600'>
|
||||
<Play className='mr-1 w-3 h-3' />
|
||||
{
|
||||
appDetail?.mode === 'advanced-chat'
|
||||
? t('workflow.common.inPreviewMode')
|
||||
: t('workflow.common.inRunMode')
|
||||
}
|
||||
<span className='mx-1'>·</span>
|
||||
<span className='text-gray-500'>Test Run#2</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(RunningTitle)
|
||||
@ -40,6 +40,7 @@ import {
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { FeaturesProvider } from '@/app/components/base/features'
|
||||
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||
import { fetchCollectionList } from '@/service/tools'
|
||||
|
||||
const nodeTypes = {
|
||||
@ -205,9 +206,24 @@ const WorkflowWrap: FC<WorkflowProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
const features = data?.features || {}
|
||||
const initialFeatures: FeaturesData = {
|
||||
opening: {
|
||||
enabled: !!features.opening_statement,
|
||||
opening_statement: features.opening_statement,
|
||||
suggested_questions: features.suggested_questions,
|
||||
},
|
||||
suggested: features.suggested_questions_after_answer || { enabled: false },
|
||||
speech2text: features.speech_to_text || { enabled: false },
|
||||
text2speech: features.text_to_speech || { enabled: false },
|
||||
citation: features.retriever_resource || { enabled: false },
|
||||
moderation: features.sensitive_word_avoidance || { enabled: false },
|
||||
annotation: features.annotation_reply || { enabled: false },
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<FeaturesProvider>
|
||||
<FeaturesProvider features={initialFeatures}>
|
||||
<Workflow
|
||||
nodes={nodesData}
|
||||
edges={edgesData}
|
||||
|
||||
@ -48,7 +48,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
}, [handleNodeDataUpdate, id, data])
|
||||
|
||||
return (
|
||||
<div className='mr-2 w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
||||
<div className='w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
||||
<div className='sticky top-0 bg-white border-b-[0.5px] border-black/5 z-10'>
|
||||
<div className='flex items-center px-4 pt-4 pb-1'>
|
||||
{
|
||||
|
||||
@ -15,7 +15,7 @@ import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
|
||||
const Panel: FC = () => {
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const runStaus = useStore(state => state.runStaus)
|
||||
const runTaskId = useStore(state => state.runTaskId)
|
||||
const nodes = useNodes<CommonNodeType>()
|
||||
const selectedNode = nodes.find(node => node.data._selected)
|
||||
const showRunHistory = useStore(state => state.showRunHistory)
|
||||
@ -25,16 +25,16 @@ const Panel: FC = () => {
|
||||
showDebugAndPreviewPanel,
|
||||
} = useMemo(() => {
|
||||
return {
|
||||
showWorkflowInfoPanel: appDetail?.mode === 'workflow' && !selectedNode,
|
||||
showNodePanel: !!selectedNode,
|
||||
showDebugAndPreviewPanel: appDetail?.mode === 'advanced-chat' && !selectedNode,
|
||||
showWorkflowInfoPanel: appDetail?.mode === 'workflow' && !selectedNode && !runTaskId,
|
||||
showNodePanel: !!selectedNode && !runTaskId,
|
||||
showDebugAndPreviewPanel: appDetail?.mode === 'advanced-chat' && !selectedNode && !runTaskId,
|
||||
}
|
||||
}, [selectedNode, appDetail])
|
||||
}, [selectedNode, appDetail, runTaskId])
|
||||
|
||||
return (
|
||||
<div className='absolute top-14 right-0 bottom-2 flex z-10'>
|
||||
{
|
||||
runStaus && (
|
||||
runTaskId && (
|
||||
<Record />
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,45 +1,33 @@
|
||||
import { useStore } from '../store'
|
||||
import {
|
||||
CheckCircle,
|
||||
XClose,
|
||||
} from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { memo } from 'react'
|
||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
|
||||
const RunHistory = () => {
|
||||
const mode = useStore(state => state.mode)
|
||||
const setShowRunHistory = useStore(state => state.setShowRunHistory)
|
||||
const setRunStaus = useStore(state => state.setRunStaus)
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
|
||||
return (
|
||||
<div className='w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'>
|
||||
<div className='ml-2 w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'>
|
||||
<div className='flex items-center justify-between px-4 pt-3 text-base font-semibold text-gray-900'>
|
||||
Run History
|
||||
<div
|
||||
className='flex items-center justify-center w-6 h-6 cursor-pointer'
|
||||
onClick={() => setShowRunHistory(false)}
|
||||
onClick={() => useStore.setState({ showRunHistory: false })}
|
||||
>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='p-2'>
|
||||
{
|
||||
mode === 'workflow' && (
|
||||
<div className='flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'>
|
||||
<CheckCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#12B76A]' />
|
||||
<div>
|
||||
<div className='flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'>Test Run#7</div>
|
||||
<div className='flex items-center text-xs text-gray-500 leading-[18px]'>
|
||||
Evan · 2 min ago
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div
|
||||
className='flex px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
|
||||
onClick={() => setRunStaus('finished')}
|
||||
className='flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
|
||||
onClick={() => useStore.setState({ runTaskId: '1' })}
|
||||
>
|
||||
<AlertCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]' />
|
||||
{
|
||||
appDetail?.mode === 'advanced-chat' && (
|
||||
<AlertCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]' />
|
||||
)
|
||||
}
|
||||
<div>
|
||||
<div className='flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'>Test Run#6</div>
|
||||
<div className='flex items-center text-xs text-gray-500 leading-[18px]'>
|
||||
@ -52,4 +40,4 @@ const RunHistory = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export default RunHistory
|
||||
export default memo(RunHistory)
|
||||
|
||||
@ -14,7 +14,7 @@ const WorkflowInfo: FC = () => {
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className='mr-2 w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
||||
<div className='w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
||||
<div className='sticky top-0 bg-white border-b-[0.5px] border-black/5'>
|
||||
<div className='flex pt-4 px-4 pb-1'>
|
||||
<AppIcon
|
||||
|
||||
@ -5,38 +5,43 @@ import type {
|
||||
ToolInWorkflow,
|
||||
ToolsMap,
|
||||
} from './block-selector/types'
|
||||
import { Mode } from './types'
|
||||
|
||||
type State = {
|
||||
mode: string
|
||||
mode: Mode
|
||||
runTaskId: string
|
||||
showRunHistory: boolean
|
||||
showFeaturesPanel: boolean
|
||||
runStaus: string
|
||||
isDragging: boolean
|
||||
helpLine?: HelpLinePosition
|
||||
toolsets: CollectionWithExpanded[]
|
||||
toolsMap: ToolsMap
|
||||
draftUpdatedAt: number
|
||||
publishedAt: number
|
||||
}
|
||||
|
||||
type Action = {
|
||||
setMode: (mode: Mode) => void
|
||||
setRunTaskId: (runTaskId: string) => void
|
||||
setShowRunHistory: (showRunHistory: boolean) => void
|
||||
setShowFeaturesPanel: (showFeaturesPanel: boolean) => void
|
||||
setRunStaus: (runStaus: string) => void
|
||||
setIsDragging: (isDragging: boolean) => void
|
||||
setHelpLine: (helpLine?: HelpLinePosition) => void
|
||||
setToolsets: (toolsets: CollectionWithExpanded[]) => void
|
||||
setToolsMap: (toolsMap: Record<string, ToolInWorkflow[]>) => void
|
||||
setDraftUpdatedAt: (draftUpdatedAt: number) => void
|
||||
setPublishedAt: (publishedAt: number) => void
|
||||
}
|
||||
|
||||
export const useStore = create<State & Action>(set => ({
|
||||
mode: 'workflow',
|
||||
mode: Mode.Editing,
|
||||
runTaskId: '',
|
||||
setRunTaskId: runTaskId => set(() => ({ runTaskId })),
|
||||
setMode: mode => set(() => ({ mode })),
|
||||
showRunHistory: false,
|
||||
setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })),
|
||||
showFeaturesPanel: false,
|
||||
setShowFeaturesPanel: showFeaturesPanel => set(() => ({ showFeaturesPanel })),
|
||||
runStaus: '',
|
||||
setRunStaus: runStaus => set(() => ({ runStaus })),
|
||||
isDragging: false,
|
||||
setIsDragging: isDragging => set(() => ({ isDragging })),
|
||||
helpLine: undefined,
|
||||
@ -47,4 +52,6 @@ export const useStore = create<State & Action>(set => ({
|
||||
setToolsMap: toolsMap => set(() => ({ toolsMap })),
|
||||
draftUpdatedAt: 0,
|
||||
setDraftUpdatedAt: draftUpdatedAt => set(() => ({ draftUpdatedAt })),
|
||||
publishedAt: 0,
|
||||
setPublishedAt: publishedAt => set(() => ({ publishedAt })),
|
||||
}))
|
||||
|
||||
@ -139,3 +139,8 @@ export type NodeDefault<T> = {
|
||||
}
|
||||
|
||||
export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => void
|
||||
|
||||
export enum Mode {
|
||||
Editing = 'editing',
|
||||
Running = 'running',
|
||||
}
|
||||
|
||||
@ -1,110 +0,0 @@
|
||||
import type { FC } from 'react'
|
||||
import {
|
||||
Fragment,
|
||||
memo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useReactFlow } from 'reactflow'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { SearchLg } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
|
||||
const ZOOM_IN_OUT_OPTIONS = [
|
||||
[
|
||||
{
|
||||
key: 'in',
|
||||
text: 'Zoom In',
|
||||
},
|
||||
{
|
||||
key: 'out',
|
||||
text: 'Zoom Out',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
key: 'to50',
|
||||
text: 'Zoom to 50%',
|
||||
},
|
||||
{
|
||||
key: 'to100',
|
||||
text: 'Zoom to 100%',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
key: 'fit',
|
||||
text: 'Zoom to Fit',
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
const ZoomInOut: FC = () => {
|
||||
const reactFlow = useReactFlow()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const handleZoom = (type: string) => {
|
||||
if (type === 'in')
|
||||
reactFlow.zoomIn()
|
||||
|
||||
if (type === 'out')
|
||||
reactFlow.zoomOut()
|
||||
|
||||
if (type === 'fit')
|
||||
reactFlow.fitView()
|
||||
}
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
placement='top-start'
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
offset={4}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild onClick={() => setOpen(v => !v)}>
|
||||
<div className={`
|
||||
absolute left-6 bottom-6
|
||||
flex items-center px-2.5 h-9 cursor-pointer rounded-lg border-[0.5px] border-gray-100 bg-white shadow-lg
|
||||
text-[13px] text-gray-500 z-10
|
||||
`}>
|
||||
<SearchLg className='mr-1 w-4 h-4' />
|
||||
100%
|
||||
<ChevronDown className='ml-1 w-4 h-4' />
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent>
|
||||
<div className='w-[168px] rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg'>
|
||||
{
|
||||
ZOOM_IN_OUT_OPTIONS.map((options, i) => (
|
||||
<Fragment key={i}>
|
||||
{
|
||||
i !== 0 && (
|
||||
<div className='h-[1px] bg-gray-100' />
|
||||
)
|
||||
}
|
||||
<div className='p-1'>
|
||||
{
|
||||
options.map(option => (
|
||||
<div
|
||||
key={option.key}
|
||||
className='flex items-center px-3 h-8 rounded-lg hover:bg-gray-50 cursor-pointer text-sm text-gray-700'
|
||||
onClick={() => handleZoom(option.key)}
|
||||
>
|
||||
{option.text}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Fragment>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ZoomInOut)
|
||||
Reference in New Issue
Block a user