11import { useState , useEffect , useRef } from 'react'
22import PropTypes from 'prop-types'
3- import { FormControl , OutlinedInput , InputBase , Popover } from '@mui/material'
3+ import { FormControl , OutlinedInput , InputBase , Popover , InputAdornment , IconButton } from '@mui/material'
44import { useTheme } from '@mui/material/styles'
5+ import Visibility from '@mui/icons-material/Visibility'
6+ import VisibilityOff from '@mui/icons-material/VisibilityOff'
57import SelectVariable from '@/ui-component/json/SelectVariable'
68import { getAvailableNodesForVariable } from '@/utils/genericHelper'
79
@@ -10,7 +12,13 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
1012 const [ myValue , setMyValue ] = useState ( value ?? '' )
1113 const [ anchorEl , setAnchorEl ] = useState ( null )
1214 const [ availableNodesForVariable , setAvailableNodesForVariable ] = useState ( [ ] )
15+ const [ isPasswordVisible , setIsPasswordVisible ] = useState ( false )
1316 const ref = useRef ( null )
17+ const inputElementRef = useRef ( null )
18+ const selectionRangeRef = useRef ( { start : null , end : null } )
19+
20+ const isPasswordField = inputParam ?. type === 'password'
21+ const hasPasswordToggle = isPasswordField && ! ! inputParam ?. enablePasswordToggle
1422
1523 const openPopOver = Boolean ( anchorEl )
1624
@@ -52,6 +60,28 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
5260 }
5361 } , [ myValue ] )
5462
63+ useEffect ( ( ) => {
64+ if ( ! hasPasswordToggle ) return
65+ const { start, end } = selectionRangeRef . current
66+ if ( start === null || end === null || ! inputElementRef . current ) return
67+
68+ requestAnimationFrame ( ( ) => {
69+ inputElementRef . current ?. focus ( )
70+ inputElementRef . current ?. setSelectionRange ( start , end )
71+ } )
72+ } , [ hasPasswordToggle , isPasswordVisible ] )
73+
74+ const handleTogglePasswordVisibility = ( ) => {
75+ const inputElement = inputElementRef . current
76+ if ( inputElement ) {
77+ selectionRangeRef . current = {
78+ start : inputElement . selectionStart ,
79+ end : inputElement . selectionEnd
80+ }
81+ }
82+ setIsPasswordVisible ( ( prev ) => ! prev )
83+ }
84+
5585 return (
5686 < >
5787 { inputParam . name === 'note' ? (
@@ -99,12 +129,13 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
99129 id = { inputParam . name }
100130 size = 'small'
101131 disabled = { disabled }
102- type = { getInputType ( inputParam . type ) }
132+ type = { hasPasswordToggle ? ( isPasswordVisible ? 'text' : 'password' ) : getInputType ( inputParam . type ) }
103133 placeholder = { inputParam . placeholder }
104134 multiline = { ! ! inputParam . rows }
105135 rows = { inputParam . rows ?? 1 }
106136 value = { myValue }
107137 name = { inputParam . name }
138+ inputRef = { inputElementRef }
108139 onChange = { ( e ) => {
109140 setMyValue ( e . target . value )
110141 onChange ( e . target . value )
@@ -118,6 +149,20 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
118149 height : inputParam . rows ? '90px' : 'inherit'
119150 }
120151 } }
152+ endAdornment = {
153+ hasPasswordToggle ? (
154+ < InputAdornment position = 'end' >
155+ < IconButton
156+ edge = 'end'
157+ onClick = { handleTogglePasswordVisibility }
158+ onMouseDown = { ( e ) => e . preventDefault ( ) }
159+ aria-label = { isPasswordVisible ? 'Hide password' : 'Show password' }
160+ >
161+ { isPasswordVisible ? < VisibilityOff fontSize = 'small' /> : < Visibility fontSize = 'small' /> }
162+ </ IconButton >
163+ </ InputAdornment >
164+ ) : undefined
165+ }
121166 sx = { {
122167 '& .MuiOutlinedInput-notchedOutline' : {
123168 borderColor : theme . palette . grey [ 900 ] + 25
0 commit comments