11import PropTypes from 'prop-types'
2- import { useRef , useContext , useState } from 'react'
2+ import { useRef , useContext , useState , useEffect } from 'react'
33import { useSelector } from 'react-redux'
4- import { NodeToolbar } from 'reactflow'
4+ import { NodeToolbar , NodeResizer } from 'reactflow'
5+ import { IconCopy , IconTrash , IconPalette } from '@tabler/icons-react'
6+ import { SketchPicker } from 'react-color'
57
68// material-ui
79import { styled , useTheme , alpha , darken , lighten } from '@mui/material/styles'
10+ import { ButtonGroup , IconButton , Box , Popover } from '@mui/material'
811
912// project imports
10- import { ButtonGroup , IconButton , Box } from '@mui/material'
11- import { IconCopy , IconTrash } from '@tabler/icons-react'
1213import { Input } from '@/ui-component/input/Input'
1314import MainCard from '@/ui-component/cards/MainCard'
1415
1516// const
1617import { flowContext } from '@/store/context/ReactFlowContext'
1718
19+ const MIN_WIDTH = 240
20+ const MIN_HEIGHT = 65
21+
1822const CardWrapper = styled ( MainCard ) ( ( { theme } ) => ( {
1923 background : theme . palette . card . main ,
2024 color : theme . darkTextPrimary ,
@@ -41,10 +45,22 @@ const StickyNote = ({ data }) => {
4145 const { reactFlowInstance, deleteNode, duplicateNode } = useContext ( flowContext )
4246 const [ inputParam ] = data . inputParams
4347 const [ isHovered , setIsHovered ] = useState ( false )
48+ const [ openToolbar , setOpenToolbar ] = useState ( false )
49+ const [ colorPickerAnchor , setColorPickerAnchor ] = useState ( null )
50+ const [ width , setWidth ] = useState ( data . inputs . size ?. width || MIN_WIDTH )
51+ const [ height , setHeight ] = useState ( data . inputs . size ?. height || MIN_HEIGHT )
4452
4553 const defaultColor = '#666666' // fallback color if data.color is not present
4654 const nodeColor = data . color || defaultColor
4755
56+ useEffect ( ( ) => {
57+ if ( data . selected ) {
58+ setOpenToolbar ( true )
59+ } else {
60+ setOpenToolbar ( false )
61+ }
62+ } , [ data . selected ] )
63+
4864 // Get different shades of the color based on state
4965 const getStateColor = ( ) => {
5066 if ( data . selected ) return nodeColor
@@ -59,73 +75,148 @@ const StickyNote = ({ data }) => {
5975 return isHovered ? lighten ( nodeColor , 0.8 ) : lighten ( nodeColor , 0.9 )
6076 }
6177
78+ const handleSizeChange = ( newWidth , newHeight ) => {
79+ setWidth ( newWidth )
80+ setHeight ( newHeight )
81+ data . inputs . size = { width : newWidth , height : newHeight }
82+ }
83+
84+ const handleColorPickerOpen = ( event ) => {
85+ setColorPickerAnchor ( event . currentTarget )
86+ }
87+
88+ const handleColorPickerClose = ( ) => {
89+ setColorPickerAnchor ( null )
90+ }
91+
92+ const handleColorChange = ( color ) => {
93+ data . color = color . hex
94+ }
95+
96+ const openColorPicker = Boolean ( colorPickerAnchor )
97+
6298 return (
63- < div ref = { ref } onMouseEnter = { ( ) => setIsHovered ( true ) } onMouseLeave = { ( ) => setIsHovered ( false ) } >
64- < StyledNodeToolbar >
65- < ButtonGroup sx = { { gap : 1 } } variant = 'outlined' aria-label = 'Basic button group' >
66- < IconButton
67- size = { 'small' }
68- title = 'Duplicate'
69- onClick = { ( ) => {
70- duplicateNode ( data . id )
71- } }
72- sx = { {
73- color : customization . isDarkMode ? 'white' : 'inherit' ,
74- '&:hover' : {
75- color : theme . palette . primary . main
76- }
77- } }
78- >
79- < IconCopy size = { 20 } />
80- </ IconButton >
81- < IconButton
82- size = { 'small' }
83- title = 'Delete'
84- onClick = { ( ) => {
85- deleteNode ( data . id )
86- } }
99+ < >
100+ < NodeResizer
101+ color = 'transparent'
102+ isVisible = { true }
103+ minWidth = { MIN_WIDTH }
104+ minHeight = { MIN_HEIGHT }
105+ onResize = { ( _event , direction ) => {
106+ handleSizeChange ( direction . width , direction . height )
107+ } }
108+ onResizeEnd = { ( _event , direction ) => {
109+ handleSizeChange ( direction . width , direction . height )
110+ } }
111+ />
112+ < div ref = { ref } onMouseEnter = { ( ) => setIsHovered ( true ) } onMouseLeave = { ( ) => setIsHovered ( false ) } >
113+ < StyledNodeToolbar isVisible = { openToolbar || openColorPicker } >
114+ < ButtonGroup sx = { { gap : 1 } } variant = 'outlined' aria-label = 'Basic button group' >
115+ < IconButton
116+ size = { 'small' }
117+ title = 'Change Color'
118+ onClick = { handleColorPickerOpen }
119+ sx = { {
120+ color : customization . isDarkMode ? 'white' : 'inherit' ,
121+ '&:hover' : {
122+ color : theme . palette . primary . main
123+ }
124+ } }
125+ >
126+ < IconPalette size = { 20 } />
127+ </ IconButton >
128+ < IconButton
129+ size = { 'small' }
130+ title = 'Duplicate'
131+ onClick = { ( ) => {
132+ duplicateNode ( data . id )
133+ } }
134+ sx = { {
135+ color : customization . isDarkMode ? 'white' : 'inherit' ,
136+ '&:hover' : {
137+ color : theme . palette . primary . main
138+ }
139+ } }
140+ >
141+ < IconCopy size = { 20 } />
142+ </ IconButton >
143+ < IconButton
144+ size = { 'small' }
145+ title = 'Delete'
146+ onClick = { ( ) => {
147+ deleteNode ( data . id )
148+ } }
149+ sx = { {
150+ color : customization . isDarkMode ? 'white' : 'inherit' ,
151+ '&:hover' : {
152+ color : theme . palette . error . main
153+ }
154+ } }
155+ >
156+ < IconTrash size = { 20 } />
157+ </ IconButton >
158+ </ ButtonGroup >
159+ </ StyledNodeToolbar >
160+ < CardWrapper
161+ content = { false }
162+ sx = { {
163+ borderColor : getStateColor ( ) ,
164+ borderWidth : '1px' ,
165+ boxShadow : data . selected ? `0 0 0 1px ${ getStateColor ( ) } !important` : 'none' ,
166+ width : width ,
167+ height : height ,
168+ backgroundColor : getBackgroundColor ( ) ,
169+ overflow : 'hidden' ,
170+ '&:hover' : {
171+ boxShadow : data . selected ? `0 0 0 1px ${ getStateColor ( ) } !important` : 'none'
172+ }
173+ } }
174+ border = { false }
175+ >
176+ < Box
87177 sx = { {
88- color : customization . isDarkMode ? 'white' : 'inherit ',
89- '&:hover' : {
90- color : theme . palette . error . main
91- }
178+ width : '100% ',
179+ height : '100%' ,
180+ overflowX : 'hidden' ,
181+ overflowY : 'auto'
92182 } }
93183 >
94- < IconTrash size = { 20 } />
95- </ IconButton >
96- </ ButtonGroup >
97- </ StyledNodeToolbar >
98- < CardWrapper
99- content = { false }
100- sx = { {
101- borderColor : getStateColor ( ) ,
102- borderWidth : '1px' ,
103- boxShadow : data . selected ? `0 0 0 1px ${ getStateColor ( ) } !important` : 'none' ,
104- minHeight : 60 ,
105- height : 'auto' ,
106- backgroundColor : getBackgroundColor ( ) ,
107- display : 'flex' ,
108- alignItems : 'center' ,
109- '&:hover' : {
110- boxShadow : data . selected ? `0 0 0 1px ${ getStateColor ( ) } !important` : 'none'
184+ < Input
185+ key = { data . id }
186+ placeholder = { inputParam . placeholder }
187+ inputParam = { inputParam }
188+ onChange = { ( newValue ) => ( data . inputs [ inputParam . name ] = newValue ) }
189+ value = { data . inputs [ inputParam . name ] ?? inputParam . default ?? '' }
190+ nodes = { reactFlowInstance ? reactFlowInstance . getNodes ( ) : [ ] }
191+ edges = { reactFlowInstance ? reactFlowInstance . getEdges ( ) : [ ] }
192+ nodeId = { data . id }
193+ />
194+ </ Box >
195+ </ CardWrapper >
196+ </ div >
197+ < Popover
198+ open = { openColorPicker }
199+ anchorEl = { colorPickerAnchor }
200+ onClose = { handleColorPickerClose }
201+ anchorOrigin = { {
202+ vertical : 'top' ,
203+ horizontal : 'right'
204+ } }
205+ transformOrigin = { {
206+ vertical : 'top' ,
207+ horizontal : 'left'
208+ } }
209+ slotProps = { {
210+ paper : {
211+ sx : {
212+ marginLeft : '16px'
213+ }
111214 }
112215 } }
113- border = { false }
114216 >
115- < Box >
116- < Input
117- key = { data . id }
118- placeholder = { inputParam . placeholder }
119- inputParam = { inputParam }
120- onChange = { ( newValue ) => ( data . inputs [ inputParam . name ] = newValue ) }
121- value = { data . inputs [ inputParam . name ] ?? inputParam . default ?? '' }
122- nodes = { reactFlowInstance ? reactFlowInstance . getNodes ( ) : [ ] }
123- edges = { reactFlowInstance ? reactFlowInstance . getEdges ( ) : [ ] }
124- nodeId = { data . id }
125- />
126- </ Box >
127- </ CardWrapper >
128- </ div >
217+ < SketchPicker color = { nodeColor } onChange = { handleColorChange } />
218+ </ Popover >
219+ </ >
129220 )
130221}
131222
0 commit comments