1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import * as React from 'react';
    
  11. import {useCallback, useState} from 'react';
    
  12. import AutoSizeInput from './NativeStyleEditor/AutoSizeInput';
    
  13. import styles from './EditableName.css';
    
  14. 
    
  15. type Type = 'props' | 'state' | 'context' | 'hooks';
    
  16. type OverrideNameFn = (
    
  17.   oldName: Array<string | number>,
    
  18.   newName: Array<string | number>,
    
  19. ) => void;
    
  20. 
    
  21. type EditableNameProps = {
    
  22.   allowEmpty?: boolean,
    
  23.   allowWhiteSpace?: boolean,
    
  24.   autoFocus?: boolean,
    
  25.   className?: string,
    
  26.   initialValue?: string,
    
  27.   overrideName: OverrideNameFn,
    
  28.   path: Array<string | number>,
    
  29.   type: Type,
    
  30. };
    
  31. 
    
  32. export default function EditableName({
    
  33.   allowEmpty = false,
    
  34.   allowWhiteSpace = false,
    
  35.   autoFocus = false,
    
  36.   className = '',
    
  37.   initialValue = '',
    
  38.   overrideName,
    
  39.   path,
    
  40.   type,
    
  41. }: EditableNameProps): React.Node {
    
  42.   const [editableName, setEditableName] = useState(initialValue);
    
  43.   const [isValid, setIsValid] = useState(false);
    
  44. 
    
  45.   const handleChange = useCallback(
    
  46.     ({target}: $FlowFixMe) => {
    
  47.       let value = target.value;
    
  48.       if (!allowWhiteSpace) {
    
  49.         value = value.trim();
    
  50.       }
    
  51. 
    
  52.       if (allowEmpty || value !== '') {
    
  53.         setIsValid(true);
    
  54.       } else {
    
  55.         setIsValid(false);
    
  56.       }
    
  57. 
    
  58.       setEditableName(value);
    
  59.     },
    
  60.     [overrideName],
    
  61.   );
    
  62. 
    
  63.   const handleKeyDown = useCallback(
    
  64.     (event: $FlowFixMe) => {
    
  65.       // Prevent keydown events from e.g. change selected element in the tree
    
  66.       event.stopPropagation();
    
  67. 
    
  68.       switch (event.key) {
    
  69.         case 'Enter':
    
  70.         case 'Tab':
    
  71.           if (isValid) {
    
  72.             const basePath = path.slice(0, path.length - 1);
    
  73.             overrideName(
    
  74.               [...basePath, initialValue],
    
  75.               [...basePath, editableName],
    
  76.             );
    
  77.           }
    
  78.           break;
    
  79.         case 'Escape':
    
  80.           setEditableName(initialValue);
    
  81.           break;
    
  82.         default:
    
  83.           break;
    
  84.       }
    
  85.     },
    
  86.     [editableName, setEditableName, isValid, initialValue, overrideName],
    
  87.   );
    
  88. 
    
  89.   return (
    
  90.     <AutoSizeInput
    
  91.       autoFocus={autoFocus}
    
  92.       className={[styles.Input, className].join(' ')}
    
  93.       onChange={handleChange}
    
  94.       onKeyDown={handleKeyDown}
    
  95.       placeholder="new entry"
    
  96.       testName="EditableName"
    
  97.       type="text"
    
  98.       value={editableName}
    
  99.     />
    
  100.   );
    
  101. }