BookmarkItemRenameInput.tsx 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import type { ChangeEvent, JSX } from 'react';
  2. import { useCallback, useRef, useState } from 'react';
  3. import { useRect } from '@growi/ui/dist/utils';
  4. import { useTranslation } from 'next-i18next';
  5. import type { AutosizeInputProps } from 'react-input-autosize';
  6. import { debounce } from 'throttle-debounce';
  7. import type { InputValidationResult } from '~/client/util/use-input-validator';
  8. import {
  9. useInputValidator,
  10. ValidationTarget,
  11. } from '~/client/util/use-input-validator';
  12. import {
  13. AutosizeSubmittableInput,
  14. getAdjustedMaxWidthForAutosizeInput,
  15. } from '../Common/SubmittableInput';
  16. import type { SubmittableInputProps } from '../Common/SubmittableInput/types';
  17. type Props = Pick<
  18. SubmittableInputProps<AutosizeInputProps>,
  19. 'value' | 'onSubmit' | 'onCancel'
  20. >;
  21. export const BookmarkItemRenameInput = (props: Props): JSX.Element => {
  22. const { t } = useTranslation();
  23. const { value, onSubmit, onCancel } = props;
  24. const parentRef = useRef<HTMLDivElement>(null);
  25. const [parentRect] = useRect(parentRef);
  26. const [validationResult, setValidationResult] =
  27. useState<InputValidationResult>();
  28. const inputValidator = useInputValidator(ValidationTarget.PAGE);
  29. const changeHandler = useCallback(
  30. async (e: ChangeEvent<HTMLInputElement>) => {
  31. const validationResult = inputValidator(e.target.value);
  32. setValidationResult(validationResult ?? undefined);
  33. },
  34. [inputValidator],
  35. );
  36. const changeHandlerDebounced = debounce(300, changeHandler);
  37. const cancelHandler = useCallback(() => {
  38. setValidationResult(undefined);
  39. onCancel?.();
  40. }, [onCancel]);
  41. const isInvalid = validationResult != null;
  42. const maxWidth =
  43. parentRect != null
  44. ? getAdjustedMaxWidthForAutosizeInput(
  45. parentRect.width,
  46. 'md',
  47. validationResult != null ? false : undefined,
  48. )
  49. : undefined;
  50. return (
  51. <div className="flex-fill" ref={parentRef}>
  52. <AutosizeSubmittableInput
  53. value={value}
  54. inputClassName={`form-control ${isInvalid ? 'is-invalid' : ''}`}
  55. inputStyle={{ maxWidth }}
  56. placeholder={t('Input page name')}
  57. aria-describedby={
  58. isInvalid ? 'bookmark-item-rename-input-feedback' : undefined
  59. }
  60. autoFocus
  61. onChange={changeHandlerDebounced}
  62. onSubmit={onSubmit}
  63. onCancel={cancelHandler}
  64. />
  65. {isInvalid && (
  66. <div
  67. id="bookmark-item-rename-input-feedback"
  68. className="invalid-feedback d-block my-1"
  69. >
  70. {validationResult.message}
  71. </div>
  72. )}
  73. </div>
  74. );
  75. };