editableTabTitle.tsx 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import {useEffect, useRef, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {GrowingInput} from 'sentry/components/growingInput';
  4. function EditableTabTitle({
  5. label,
  6. onChange,
  7. isEditing,
  8. setIsEditing,
  9. }: {
  10. isEditing: boolean;
  11. label: string;
  12. onChange: (newLabel: string) => void;
  13. setIsEditing: (isEditing: boolean) => void;
  14. }) {
  15. const [inputValue, setInputValue] = useState(label);
  16. const inputRef = useRef<HTMLInputElement>(null);
  17. const isEmpty = !inputValue.trim();
  18. const handleOnBlur = () => {
  19. const trimmedInputValue = inputValue.trim();
  20. if (!isEditing) {
  21. return;
  22. }
  23. if (isEmpty) {
  24. setInputValue(label);
  25. setIsEditing(false);
  26. return;
  27. }
  28. if (trimmedInputValue !== label) {
  29. onChange(trimmedInputValue);
  30. setInputValue(trimmedInputValue);
  31. }
  32. setIsEditing(false);
  33. };
  34. const handleOnKeyDown = (e: React.KeyboardEvent) => {
  35. if (e.key === 'Enter') {
  36. handleOnBlur();
  37. }
  38. if (e.key === 'Escape') {
  39. setInputValue(label.trim());
  40. setIsEditing(false);
  41. }
  42. if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === ' ') {
  43. e.stopPropagation();
  44. }
  45. };
  46. useEffect(() => {
  47. if (isEditing) {
  48. setTimeout(() => {
  49. inputRef?.current?.focus();
  50. }, 0);
  51. }
  52. }, [isEditing, inputRef]);
  53. const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  54. setInputValue(e.target.value);
  55. };
  56. return isEditing ? (
  57. <StyledGrowingInput
  58. value={inputValue}
  59. onChange={handleOnChange}
  60. onKeyDown={handleOnKeyDown}
  61. onBlur={handleOnBlur}
  62. ref={inputRef}
  63. />
  64. ) : (
  65. <div style={{height: '20px'}}> {label}</div>
  66. );
  67. }
  68. export default EditableTabTitle;
  69. const StyledGrowingInput = styled(GrowingInput)`
  70. border: none;
  71. padding: 0;
  72. background: transparent;
  73. min-height: 0px;
  74. height: 20px;
  75. cursor: pointer;
  76. border-radius: 0px;
  77. &,
  78. &:focus,
  79. &:active,
  80. &:hover {
  81. box-shadow: none;
  82. }
  83. `;