BookmarkFolderNameInput.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import type { ChangeEvent } 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 { ValidationTarget, useInputValidator } from '~/client/util/use-input-validator';
  9. import { AutosizeSubmittableInput, getAdjustedMaxWidthForAutosizeInput } from '../Common/SubmittableInput';
  10. import type { SubmittableInputProps } from '../Common/SubmittableInput/types';
  11. type Props = Pick<SubmittableInputProps<AutosizeInputProps>, 'value' | 'onSubmit' | 'onCancel'>;
  12. export const BookmarkFolderNameInput = (props: Props): JSX.Element => {
  13. const { t } = useTranslation();
  14. const { value, onSubmit, onCancel } = props;
  15. const parentRef = useRef<HTMLDivElement>(null);
  16. const [parentRect] = useRect(parentRef);
  17. const [validationResult, setValidationResult] = useState<InputValidationResult>();
  18. const inputValidator = useInputValidator(ValidationTarget.FOLDER);
  19. const changeHandler = useCallback(async(e: ChangeEvent<HTMLInputElement>) => {
  20. const validationResult = inputValidator(e.target.value);
  21. setValidationResult(validationResult ?? undefined);
  22. }, [inputValidator]);
  23. const changeHandlerDebounced = debounce(300, changeHandler);
  24. const cancelHandler = useCallback(() => {
  25. setValidationResult(undefined);
  26. onCancel?.();
  27. }, [onCancel]);
  28. const isInvalid = validationResult != null;
  29. const maxWidth = parentRect != null
  30. ? getAdjustedMaxWidthForAutosizeInput(parentRect.width, 'md', validationResult != null ? false : undefined)
  31. : undefined;
  32. return (
  33. <div ref={parentRef}>
  34. <AutosizeSubmittableInput
  35. value={value}
  36. inputClassName={`form-control ${isInvalid ? 'is-invalid' : ''}`}
  37. inputStyle={{ maxWidth }}
  38. placeholder={t('bookmark_folder.input_placeholder')}
  39. aria-describedby={isInvalid ? 'bookmark-folder-name-input-feedback' : undefined}
  40. autoFocus
  41. onChange={changeHandlerDebounced}
  42. onSubmit={onSubmit}
  43. onCancel={cancelHandler}
  44. />
  45. { isInvalid && (
  46. <div id="bookmark-folder-name-input-feedback" className="invalid-feedback d-block my-1">
  47. {validationResult.message}
  48. </div>
  49. ) }
  50. </div>
  51. );
  52. };