import styled from 'styled-components' ; import type { MutableRefObject } from 'react' ; import React , { useMemo , useRef } from 'react' ; import mergeRefs from 'react-merge-refs' ; import useRefAndState from '@theatre/studio/utils/useRefAndState' ; import useOnClickOutside from '@theatre/studio/uiComponents/useOnClickOutside' ; const Input = styled . input . attrs ( { type : 'text' } ) `
  background: transparent;
  border: 1px solid transparent;
  color: rgba(255, 255, 255, 0.9);
  padding: 1px 6px;
  font: inherit;
  outline: none;
  cursor: text;
  text-align: left;
  width: 100%;
  height: calc(100% - 4px);
  border-radius: 2px;
  border: 1px solid transparent;
  box-sizing: border-box;

  &:hover {
    background-color: #10101042;
    border-color: #00000059;
  }

  &:hover,
  &:focus {
    cursor: text;
    background-color: #10101042;
    border-color: #00000059;
  }

  &.invalid {
    border-color: red;
  }
` ; type IState_NoFocus = { mode : 'noFocus' ; } ; type IState_EditingViaKeyboard = { mode : 'editingViaKeyboard' ; currentEditedValueInString : string ; valueBeforeEditing : string ; } ; type IState = IState_NoFocus | IState_EditingViaKeyboard ; const alwaysValid = ( v ) => true ; const BasicStringInput = ( props ) => { const [ stateRef ] = useRefAndState < IState > ( { mode : 'noFocus' } ) ; const isValid = props . isValid ?? alwaysValid ; const propsRef = useRef ( props ) ; propsRef . current = props ; const inputRef = useRef < HTMLInputElement | null > ( null ) ; useOnClickOutside ( inputRef . current , ( ) => { inputRef . current ! . blur ( ) ; } , stateRef . current . mode === 'editingViaKeyboard' ) ; const callbacks = useMemo ( ( ) => { const inputChange = ( e ) => { const target = e . target as HTMLInputElement ; const { value } = target ; const curState = stateRef . current as IState_EditingViaKeyboard ; stateRef . current = { ... curState , currentEditedValueInString : value } ; if ( ! isValid ( value ) ) return ; propsRef . current . temporarilySetValue ( value ) ; } ; const onBlur = ( ) => { if ( stateRef . current . mode === 'editingViaKeyboard' ) { commitKeyboardInput ( ) ; stateRef . current = { mode : 'noFocus' } ; } propsRef . current . onBlur ?. ( ) ; } ; const commitKeyboardInput = ( ) => { const curState = stateRef . current as IState_EditingViaKeyboard ; const value = curState . currentEditedValueInString ; if ( ! isValid ( value ) ) { propsRef . current . discardTemporaryValue ( ) ; } else { if ( curState . valueBeforeEditing === value ) { propsRef . current . discardTemporaryValue ( ) ; } else { propsRef . current . permenantlySetValue ( value ) ; } } } ; const onInputKeyDown = ( e ) => { if ( e . key === 'Escape' ) { propsRef . current . discardTemporaryValue ( ) ; stateRef . current = { mode : 'noFocus' } ; inputRef . current ! . blur ( ) ; } else if ( e . key === 'Enter' || e . key === 'Tab' ) { commitKeyboardInput ( ) ; inputRef . current ! . blur ( ) ; } } ; const onClick = ( e ) => { if ( stateRef . current . mode === 'noFocus' ) { const c = inputRef . current ! ; c . focus ( ) ; e . preventDefault ( ) ; e . stopPropagation ( ) ; } else { e . stopPropagation ( ) ; } } ; const onFocus = ( ) => { if ( stateRef . current . mode === 'noFocus' ) { transitionToEditingViaKeyboardMode ( ) ; } else if ( stateRef . current . mode === 'editingViaKeyboard' ) { } } ; const transitionToEditingViaKeyboardMode = ( ) => { const curValue = propsRef . current . value ; stateRef . current = { mode : 'editingViaKeyboard' , currentEditedValueInString : String ( curValue ) , valueBeforeEditing : curValue , } ; setTimeout ( ( ) => { inputRef . current ! . focus ( ) ; } ) ; } ; return { inputChange , onBlur , onInputKeyDown , onClick , onFocus , } ; } , [ ] ) ; let value = stateRef . current . mode !== 'editingViaKeyboard' ? format ( props . value ) : stateRef . current . currentEditedValueInString ; const _refs = [ inputRef ] ; if ( props . inputRef ) _refs . push ( props . inputRef ) ; const theInput = ( < Input key = "input" type = "text" className = { ! isValid ( value ) ? 'invalid' : '' } onChange = { callbacks . inputChange } value = { value } onBlur = { callbacks . onBlur } onKeyDown = { callbacks . onInputKeyDown } onClick = { callbacks . onClick } onFocus = { callbacks . onFocus } ref = { mergeRefs ( _refs ) } onMouseDown = { ( e ) => { e . stopPropagation ( ) ; } } onDoubleClick = { ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; } } />); return theInput ; } ; function format ( v ) { return v ; } export default BasicStringInput ;