Flowise/packages/ui/src/views/canvas/CanvasNode.js

245 lines
11 KiB
JavaScript

import PropTypes from 'prop-types'
import { useContext, useState, useEffect } from 'react'
import { useSelector } from 'react-redux'
// material-ui
import { useTheme } from '@mui/material/styles'
import { IconButton, Box, Typography, Divider, Button } from '@mui/material'
import Tooltip from '@mui/material/Tooltip'
// project imports
import NodeCardWrapper from '../../ui-component/cards/NodeCardWrapper'
import NodeTooltip from '../../ui-component/tooltip/NodeTooltip'
import NodeInputHandler from './NodeInputHandler'
import NodeOutputHandler from './NodeOutputHandler'
import AdditionalParamsDialog from 'ui-component/dialog/AdditionalParamsDialog'
import NodeInfoDialog from 'ui-component/dialog/NodeInfoDialog'
// const
import { baseURL } from 'store/constant'
import { IconTrash, IconCopy, IconInfoCircle, IconAlertTriangle } from '@tabler/icons'
import { flowContext } from 'store/context/ReactFlowContext'
// ===========================|| CANVAS NODE ||=========================== //
const CanvasNode = ({ data }) => {
const theme = useTheme()
const canvas = useSelector((state) => state.canvas)
const { deleteNode, duplicateNode } = useContext(flowContext)
const [showDialog, setShowDialog] = useState(false)
const [dialogProps, setDialogProps] = useState({})
const [showInfoDialog, setShowInfoDialog] = useState(false)
const [infoDialogProps, setInfoDialogProps] = useState({})
const [warningMessage, setWarningMessage] = useState('')
const [open, setOpen] = useState(false)
const handleClose = () => {
setOpen(false)
}
const handleOpen = () => {
setOpen(true)
}
const nodeOutdatedMessage = (oldVersion, newVersion) => `Node version ${oldVersion} outdated\nUpdate to latest version ${newVersion}`
const nodeVersionEmptyMessage = (newVersion) => `Node outdated\nUpdate to latest version ${newVersion}`
const onDialogClicked = () => {
const dialogProps = {
data,
inputParams: data.inputParams.filter((param) => param.additionalParams),
confirmButtonName: 'Save',
cancelButtonName: 'Cancel'
}
setDialogProps(dialogProps)
setShowDialog(true)
}
useEffect(() => {
const componentNode = canvas.componentNodes.find((nd) => nd.name === data.name)
if (componentNode) {
if (!data.version) {
setWarningMessage(nodeVersionEmptyMessage(componentNode.version))
} else if (data.version && componentNode.version > data.version) {
setWarningMessage(nodeOutdatedMessage(data.version, componentNode.version))
} else if (componentNode.badge === 'DEPRECATING') {
setWarningMessage('This node will be deprecated in the next release. Change to a new node tagged with NEW')
}
}
}, [canvas.componentNodes, data.name, data.version])
return (
<>
<NodeCardWrapper
content={false}
sx={{
padding: 0,
borderColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary
}}
border={false}
>
<NodeTooltip
open={!canvas.canvasDialogShow && open}
onClose={handleClose}
onOpen={handleOpen}
disableFocusListener={true}
title={
<div
style={{
background: 'transparent',
display: 'flex',
flexDirection: 'column'
}}
>
<IconButton
title='Duplicate'
onClick={() => {
duplicateNode(data.id)
}}
sx={{ height: '35px', width: '35px', '&:hover': { color: theme?.palette.primary.main } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconCopy />
</IconButton>
<IconButton
title='Delete'
onClick={() => {
deleteNode(data.id)
}}
sx={{ height: '35px', width: '35px', '&:hover': { color: 'red' } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconTrash />
</IconButton>
<IconButton
title='Info'
onClick={() => {
setInfoDialogProps({ data })
setShowInfoDialog(true)
}}
sx={{ height: '35px', width: '35px', '&:hover': { color: theme?.palette.secondary.main } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconInfoCircle />
</IconButton>
</div>
}
placement='right-start'
>
<Box>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<Box style={{ width: 50, marginRight: 10, padding: 5 }}>
<div
style={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
borderRadius: '50%',
backgroundColor: 'white',
cursor: 'grab'
}}
>
<img
style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}
src={`${baseURL}/api/v1/node-icon/${data.name}`}
alt='Notification'
/>
</div>
</Box>
<Box>
<Typography
sx={{
fontSize: '1rem',
fontWeight: 500,
mr: 2
}}
>
{data.label}
</Typography>
</Box>
{warningMessage && (
<>
<div style={{ flexGrow: 1 }}></div>
<Tooltip title={<span style={{ whiteSpace: 'pre-line' }}>{warningMessage}</span>} placement='top'>
<IconButton sx={{ height: 35, width: 35 }}>
<IconAlertTriangle size={35} color='orange' />
</IconButton>
</Tooltip>
</>
)}
</div>
{(data.inputAnchors.length > 0 || data.inputParams.length > 0) && (
<>
<Divider />
<Box sx={{ background: theme.palette.asyncSelect.main, p: 1 }}>
<Typography
sx={{
fontWeight: 500,
textAlign: 'center'
}}
>
Inputs
</Typography>
</Box>
<Divider />
</>
)}
{data.inputAnchors.map((inputAnchor, index) => (
<NodeInputHandler key={index} inputAnchor={inputAnchor} data={data} />
))}
{data.inputParams
.filter((inputParam) => !inputParam.hidden)
.map((inputParam, index) => (
<NodeInputHandler key={index} inputParam={inputParam} data={data} />
))}
{data.inputParams.find((param) => param.additionalParams) && (
<div
style={{
textAlign: 'center',
marginTop:
data.inputParams.filter((param) => param.additionalParams).length ===
data.inputParams.length + data.inputAnchors.length
? 20
: 0
}}
>
<Button sx={{ borderRadius: 25, width: '90%', mb: 2 }} variant='outlined' onClick={onDialogClicked}>
Additional Parameters
</Button>
</div>
)}
<Divider />
<Box sx={{ background: theme.palette.asyncSelect.main, p: 1 }}>
<Typography
sx={{
fontWeight: 500,
textAlign: 'center'
}}
>
Output
</Typography>
</Box>
<Divider />
{data.outputAnchors.map((outputAnchor, index) => (
<NodeOutputHandler key={index} outputAnchor={outputAnchor} data={data} />
))}
</Box>
</NodeTooltip>
</NodeCardWrapper>
<AdditionalParamsDialog
show={showDialog}
dialogProps={dialogProps}
onCancel={() => setShowDialog(false)}
></AdditionalParamsDialog>
<NodeInfoDialog show={showInfoDialog} dialogProps={infoDialogProps} onCancel={() => setShowInfoDialog(false)}></NodeInfoDialog>
</>
)
}
CanvasNode.propTypes = {
data: PropTypes.object
}
export default CanvasNode