Merge pull request #1513 from 0xi4o/feature/sticky-notes-node
FEATURE: Sticky Note node
This commit is contained in:
commit
27f2a7f6ba
|
|
@ -0,0 +1,40 @@
|
|||
import { INode, INodeParams } from '../../../src/Interface'
|
||||
|
||||
class StickyNote implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'Sticky Note'
|
||||
this.name = 'stickyNote'
|
||||
this.version = 1.0
|
||||
this.type = 'StickyNote'
|
||||
this.icon = 'stickyNote.svg'
|
||||
this.category = 'Utilities'
|
||||
this.description = 'Add a sticky note'
|
||||
this.inputs = [
|
||||
{
|
||||
label: '',
|
||||
name: 'note',
|
||||
type: 'string',
|
||||
rows: 1,
|
||||
placeholder: 'Type something here',
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
this.baseClasses = [this.type]
|
||||
}
|
||||
|
||||
async init(): Promise<any> {
|
||||
return new StickyNote()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: StickyNote }
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M15.5 3H5a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2V8.5L15.5 3Z"/>
|
||||
<path d="M15 3v6h6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 305 B |
|
|
@ -90,7 +90,7 @@ export interface INodeProperties {
|
|||
type: string
|
||||
icon: string
|
||||
version: number
|
||||
category: string
|
||||
category: string // TODO: use enum instead of string
|
||||
baseClasses: string[]
|
||||
description?: string
|
||||
filePath?: string
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/* eslint-disable */
|
||||
import { Entity, Column, CreateDateColumn, UpdateDateColumn, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { IVariable } from "../../Interface";
|
||||
import { IVariable } from '../../Interface'
|
||||
|
||||
@Entity()
|
||||
export class Variable implements IVariable{
|
||||
export class Variable implements IVariable {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
|
|
@ -13,10 +13,9 @@ export class Variable implements IVariable{
|
|||
@Column({ nullable: true, type: 'text' })
|
||||
value: string
|
||||
|
||||
@Column({default: 'string', type: 'text'})
|
||||
@Column({ default: 'string', type: 'text' })
|
||||
type: string
|
||||
|
||||
|
||||
@CreateDateColumn()
|
||||
createdDate: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
// material-ui
|
||||
import { styled } from '@mui/material/styles'
|
||||
|
||||
// project imports
|
||||
import MainCard from './MainCard'
|
||||
|
||||
const NodeCardWrapper = styled(MainCard)(({ theme }) => ({
|
||||
background: theme.palette.card.main,
|
||||
color: theme.darkTextPrimary,
|
||||
border: 'solid 1px',
|
||||
borderColor: theme.palette.primary[200] + 75,
|
||||
width: '300px',
|
||||
height: 'auto',
|
||||
padding: '10px',
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.primary.main
|
||||
}
|
||||
}))
|
||||
|
||||
export default NodeCardWrapper
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useEffect, useRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { FormControl, OutlinedInput, Popover } from '@mui/material'
|
||||
import { FormControl, OutlinedInput, InputBase, Popover } from '@mui/material'
|
||||
import SelectVariable from 'ui-component/json/SelectVariable'
|
||||
import { getAvailableNodesForVariable } from 'utils/genericHelper'
|
||||
|
||||
|
|
@ -50,29 +50,67 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, disab
|
|||
|
||||
return (
|
||||
<>
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
<OutlinedInput
|
||||
id={inputParam.name}
|
||||
size='small'
|
||||
disabled={disabled}
|
||||
type={getInputType(inputParam.type)}
|
||||
placeholder={inputParam.placeholder}
|
||||
multiline={!!inputParam.rows}
|
||||
rows={inputParam.rows ?? 1}
|
||||
value={myValue}
|
||||
name={inputParam.name}
|
||||
onChange={(e) => {
|
||||
setMyValue(e.target.value)
|
||||
onChange(e.target.value)
|
||||
}}
|
||||
inputProps={{
|
||||
step: inputParam.step ?? 1,
|
||||
style: {
|
||||
height: inputParam.rows ? '90px' : 'inherit'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
{inputParam.name === 'note' ? (
|
||||
<FormControl sx={{ width: '100%', height: 'auto' }} size='small'>
|
||||
<InputBase
|
||||
id={nodeId}
|
||||
size='small'
|
||||
disabled={disabled}
|
||||
type={getInputType(inputParam.type)}
|
||||
placeholder={inputParam.placeholder}
|
||||
multiline={!!inputParam.rows}
|
||||
minRows={inputParam.rows ?? 1}
|
||||
value={myValue}
|
||||
name={inputParam.name}
|
||||
onChange={(e) => {
|
||||
setMyValue(e.target.value)
|
||||
onChange(e.target.value)
|
||||
}}
|
||||
inputProps={{
|
||||
step: inputParam.step ?? 1,
|
||||
style: {
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
color: '#212121'
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
padding: '10px 14px',
|
||||
textarea: {
|
||||
'&::placeholder': {
|
||||
color: '#616161'
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
) : (
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
<OutlinedInput
|
||||
id={inputParam.name}
|
||||
size='small'
|
||||
disabled={disabled}
|
||||
type={getInputType(inputParam.type)}
|
||||
placeholder={inputParam.placeholder}
|
||||
multiline={!!inputParam.rows}
|
||||
rows={inputParam.rows ?? 1}
|
||||
value={myValue}
|
||||
name={inputParam.name}
|
||||
onChange={(e) => {
|
||||
setMyValue(e.target.value)
|
||||
onChange(e.target.value)
|
||||
}}
|
||||
inputProps={{
|
||||
step: inputParam.step ?? 1,
|
||||
style: {
|
||||
height: inputParam.rows ? '90px' : 'inherit'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
<div ref={ref}></div>
|
||||
{inputParam?.acceptVariable && (
|
||||
<Popover
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import { styled } from '@mui/material/styles'
|
||||
import Tooltip, { tooltipClasses } from '@mui/material/Tooltip'
|
||||
|
||||
const NodeTooltip = styled(({ className, ...props }) => <Tooltip {...props} classes={{ popper: className }} />)(({ theme }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: theme.palette.nodeToolTip.background,
|
||||
color: theme.palette.nodeToolTip.color,
|
||||
boxShadow: theme.shadows[1]
|
||||
}
|
||||
}))
|
||||
|
||||
export default NodeTooltip
|
||||
|
|
@ -3,12 +3,13 @@ import { useContext, useState, useEffect } from 'react'
|
|||
import { useSelector } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { styled, useTheme } from '@mui/material/styles'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import { IconButton, Box, Typography, Divider, Button } from '@mui/material'
|
||||
import Tooltip, { tooltipClasses } from '@mui/material/Tooltip'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
|
||||
// project imports
|
||||
import MainCard from 'ui-component/cards/MainCard'
|
||||
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'
|
||||
|
|
@ -19,28 +20,6 @@ import { baseURL } from 'store/constant'
|
|||
import { IconTrash, IconCopy, IconInfoCircle, IconAlertTriangle } from '@tabler/icons'
|
||||
import { flowContext } from 'store/context/ReactFlowContext'
|
||||
|
||||
const CardWrapper = styled(MainCard)(({ theme }) => ({
|
||||
background: theme.palette.card.main,
|
||||
color: theme.darkTextPrimary,
|
||||
border: 'solid 1px',
|
||||
borderColor: theme.palette.primary[200] + 75,
|
||||
width: '300px',
|
||||
height: 'auto',
|
||||
padding: '10px',
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.primary.main
|
||||
}
|
||||
}))
|
||||
|
||||
const LightTooltip = styled(({ className, ...props }) => <Tooltip {...props} classes={{ popper: className }} />)(({ theme }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: theme.palette.nodeToolTip.background,
|
||||
color: theme.palette.nodeToolTip.color,
|
||||
boxShadow: theme.shadows[1]
|
||||
}
|
||||
}))
|
||||
|
||||
// ===========================|| CANVAS NODE ||=========================== //
|
||||
|
||||
const CanvasNode = ({ data }) => {
|
||||
|
|
@ -93,7 +72,7 @@ const CanvasNode = ({ data }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CardWrapper
|
||||
<NodeCardWrapper
|
||||
content={false}
|
||||
sx={{
|
||||
padding: 0,
|
||||
|
|
@ -101,7 +80,7 @@ const CanvasNode = ({ data }) => {
|
|||
}}
|
||||
border={false}
|
||||
>
|
||||
<LightTooltip
|
||||
<NodeTooltip
|
||||
open={!canvas.canvasDialogShow && open}
|
||||
onClose={handleClose}
|
||||
onOpen={handleOpen}
|
||||
|
|
@ -242,13 +221,12 @@ const CanvasNode = ({ data }) => {
|
|||
</Typography>
|
||||
</Box>
|
||||
<Divider />
|
||||
|
||||
{data.outputAnchors.map((outputAnchor, index) => (
|
||||
<NodeOutputHandler key={index} outputAnchor={outputAnchor} data={data} />
|
||||
))}
|
||||
</Box>
|
||||
</LightTooltip>
|
||||
</CardWrapper>
|
||||
</NodeTooltip>
|
||||
</NodeCardWrapper>
|
||||
<AdditionalParamsDialog
|
||||
show={showDialog}
|
||||
dialogProps={dialogProps}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
import PropTypes from 'prop-types'
|
||||
import { useContext, useState } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
// project imports
|
||||
import NodeCardWrapper from '../../ui-component/cards/NodeCardWrapper'
|
||||
import NodeTooltip from '../../ui-component/tooltip/NodeTooltip'
|
||||
import { IconButton, Box } from '@mui/material'
|
||||
import { IconCopy, IconTrash } from '@tabler/icons'
|
||||
import { Input } from 'ui-component/input/Input'
|
||||
|
||||
// const
|
||||
import { flowContext } from '../../store/context/ReactFlowContext'
|
||||
|
||||
const StickyNote = ({ data }) => {
|
||||
const theme = useTheme()
|
||||
const canvas = useSelector((state) => state.canvas)
|
||||
const { deleteNode, duplicateNode } = useContext(flowContext)
|
||||
const [inputParam] = data.inputParams
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeCardWrapper
|
||||
content={false}
|
||||
sx={{
|
||||
padding: 0,
|
||||
borderColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary,
|
||||
backgroundColor: data.selected ? '#FFDC00' : '#FFE770'
|
||||
}}
|
||||
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>
|
||||
</div>
|
||||
}
|
||||
placement='right-start'
|
||||
>
|
||||
<Box>
|
||||
<Input
|
||||
key={data.id}
|
||||
inputParam={inputParam}
|
||||
onChange={(newValue) => (data.inputs[inputParam.name] = newValue)}
|
||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? ''}
|
||||
nodes={inputParam?.acceptVariable && reactFlowInstance ? reactFlowInstance.getNodes() : []}
|
||||
edges={inputParam?.acceptVariable && reactFlowInstance ? reactFlowInstance.getEdges() : []}
|
||||
nodeId={data.id}
|
||||
/>
|
||||
</Box>
|
||||
</NodeTooltip>
|
||||
</NodeCardWrapper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
StickyNote.propTypes = {
|
||||
data: PropTypes.object
|
||||
}
|
||||
|
||||
export default StickyNote
|
||||
|
|
@ -21,6 +21,7 @@ import { useTheme } from '@mui/material/styles'
|
|||
// project imports
|
||||
import CanvasNode from './CanvasNode'
|
||||
import ButtonEdge from './ButtonEdge'
|
||||
import StickyNote from './StickyNote'
|
||||
import CanvasHeader from './CanvasHeader'
|
||||
import AddNodes from './AddNodes'
|
||||
import ConfirmDialog from 'ui-component/dialog/ConfirmDialog'
|
||||
|
|
@ -46,7 +47,7 @@ import useNotifier from 'utils/useNotifier'
|
|||
// const
|
||||
import { FLOWISE_CREDENTIAL_ID } from 'store/constant'
|
||||
|
||||
const nodeTypes = { customNode: CanvasNode }
|
||||
const nodeTypes = { customNode: CanvasNode, stickyNote: StickyNote }
|
||||
const edgeTypes = { buttonedge: ButtonEdge }
|
||||
|
||||
// ==============================|| CANVAS ||============================== //
|
||||
|
|
@ -276,7 +277,7 @@ const Canvas = () => {
|
|||
const newNode = {
|
||||
id: newNodeId,
|
||||
position,
|
||||
type: 'customNode',
|
||||
type: nodeData.type !== 'StickyNote' ? 'customNode' : 'stickyNote',
|
||||
data: initNode(nodeData, newNodeId)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import { useTheme } from '@mui/material/styles'
|
|||
|
||||
// project imports
|
||||
import MarketplaceCanvasNode from './MarketplaceCanvasNode'
|
||||
|
||||
import MarketplaceCanvasHeader from './MarketplaceCanvasHeader'
|
||||
import StickyNote from '../canvas/StickyNote'
|
||||
|
||||
const nodeTypes = { customNode: MarketplaceCanvasNode }
|
||||
const nodeTypes = { customNode: MarketplaceCanvasNode, stickyNote: StickyNote }
|
||||
const edgeTypes = { buttonedge: '' }
|
||||
|
||||
// ==============================|| CANVAS ||============================== //
|
||||
|
|
|
|||
Loading…
Reference in New Issue