import ComponentStore from '@store/componentStore' ; import styles from '@styles/clui.module.css' ; import classnames from 'classnames' ; import Downshift from 'downshift' ; import { observer } from 'mobx-react' ; import React , { useCallback , useEffect , useRef , useState } from 'react' ; import PromptIcon from './promptIcon' ; import MatchSubString from './subString' ; import useCluiInput from './useCluiInput' ; const { CluiStore } = ComponentStore ; const { setInputMessage } = CluiStore ; type MenuItemProps = { item : any ; highlighted : any ; theme ? : any ; } ; type PromptProps = { command ? : { commands : { [ name ] : any ; } ; } ; item ? : any ; value ? : any ; autoRun ? : boolean ; autoFocus ? : boolean ; theme ? : any ; } ; const MenuItem = ( props ) => { const { item , highlighted } = props ; return ( < div className = { classnames ( styles . root , { highlighted } ) } > < div className = { styles . value } > { item . searchValue ? ( < MatchSubString source = { item . value } match = { item . searchValue } / > ) : ( item . value ) } < /div> { item . data && item . data . description ? ( < div className = { styles . description } > { item . data . description } < /div>) : null} < /div>); } ; const Prompt = ( props ) => { const input = useRef < HTMLInputElement | null > ( null ) ; const ran = useRef ( false ) ; const [ focused , setFocused ] = useState ( false ) ; const { command } = props as { command : { commands : { [ name ] : any ; } ; } ; } ; const { cluiFocused , setCluiFocused } = CluiStore ; useEffect ( ( ) => { setCluiFocused ( focused ) ; } , [ focused ] ) ; useEffect ( ( ) => { if ( CluiStore . ran ) { return ; } if ( cluiFocused ) { if ( input && input . current ) { input . current . focus ( ) ; } } } , [ cluiFocused ] ) ; const [ state , update ] = useCluiInput ( { command , value : props . value || '' , index : props . value ? props . value . length : 0 } ) ; const onKeyUp = useCallback ( e => update ( { index : e . target . selectionStart } ) , [ update ] ) ; const run = useCallback ( ( ) => { if ( ! props . item || ! state . run ) { return ; } ran . current = true ; if ( input . current ) { const { value } = input . current ; if ( value !== 'Clear' ) { setInputMessage ( value ) ; } input . current . blur ( ) ; } props . item . insertAfter ( state . run ( ) , < Prompt { ... props } autoRun = { false } value = "" / > ) . next ( ) ; } , [ props , state ] ) ; useEffect ( ( ) => { if ( ran . current ) { return ; } if ( props . autoRun && state . run ) { run ( ) ; } } , [ props . autoRun , state . run , run ] ) ; useEffect ( ( ) => { if ( input . current && props . autoFocus ) { const { value } = input . current ; input . current . focus ( ) ; input . current . selectionStart = value . length ; } } , [ props . autoFocus , input . current ] ) ; const isLastSession = props . item && props . item . index === props . item . session . currentIndex ; return ( < Downshift defaultHighlightedIndex = { 0 } initialHighlightedIndex = { 0 } inputValue = { state . value } onChange = { option => { if ( ! option ) { return ; } update ( { value : `${ option . inputValue } ` , index : option . cursorTarget + 1 } ) ; } } itemToString = { ( ) => state . value } > { ds => { const inputProps = ds . getInputProps ( { autoFocus : true , spellCheck : false , autoComplete : 'on' , id : 'clui-input' , placeholder : 'Set your configuration here... (? for help)' , onFocus : ( ) => setFocused ( true ) , onBlur : ( ) => setFocused ( false ) , onKeyUp , onChange : ( { currentTarget } ) => { update ( { value : currentTarget . value , index : currentTarget . selectionStart || 0 } ) ; } , onKeyDown : ( event ) => { if ( event . key === 'Enter' ) { if ( state . run ) { run ( ) ; return ; } if ( ds . highlightedIndex !== undefined ) { ds . selectHighlightedItem ( ) ; } } if ( event . key === 'ArrowUp' && ds . highlightedIndex === 0 ) { event . nativeEvent . preventDownshiftDefault = true ; event . preventDefault ( ) ; ds . setState ( { highlightedIndex : null } ) ; } } } ) ; return ( < div className = { classnames ( styles . prompt , { active : isLastSession || focused } ) } > < PromptIcon /> < div className = { styles . input } > { ( isLastSession || focused ) && state . run ? ( < div className = { styles [ 'input-shadow' ] } > < span > { state . value } < /span> < button type = "button" onClick = { run } > run ↵ < /button> < /div>) : null} < input className = "clui-input" style = { { wordWrap : 'break-word' } } ref = { input } { ... inputProps } /> { focused ? ( < div className = { styles [ 'menu-anchor' ] } > < div className = { styles . menu } > < div className = { styles [ 'menu-offset' ] } > { state . value . slice ( 0 , state . nodeStart || 0 ) } < /div> < ul { ... ds . getMenuProps ( ) } > { state . options . map ( ( item , index ) => ( < li { ... ds . getItemProps ( { item } ) } key = { item . value } className = { classnames ( 'item' , { active : ds . highlightedIndex === index } ) } > < MenuItem item = { item } highlighted = { ds . highlightedIndex === index } theme = { props . theme } / > < /li>))} < /ul> < /div> < /div>) : null} < /div> < /div>); } } < /Downshift>); } ; export default observer ( Prompt ) ;