file-tree-create-name-input.jsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { useRef, useEffect } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import { useFileTreeCreateName } from '../../contexts/file-tree-create-name'
  4. import PropTypes from 'prop-types'
  5. import {
  6. BlockedFilenameError,
  7. DuplicateFilenameError,
  8. InvalidFilenameError,
  9. } from '../../errors'
  10. import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
  11. import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
  12. import OLFormControl from '@/features/ui/components/ol/ol-form-control'
  13. import OLNotification from '@/features/ui/components/ol/ol-notification'
  14. /**
  15. * A form component that renders a text input with label,
  16. * plus a validation warning and/or an error message when needed
  17. */
  18. export default function FileTreeCreateNameInput({
  19. label,
  20. focusName = false,
  21. classes = {},
  22. placeholder,
  23. error,
  24. inFlight,
  25. }) {
  26. const { t } = useTranslation()
  27. // the value is stored in a context provider, so it's available elsewhere in the form
  28. const { name, setName, touchedName, validName } = useFileTreeCreateName()
  29. // focus the first part of the filename if needed
  30. const inputRef = useRef(null)
  31. useEffect(() => {
  32. if (inputRef.current && focusName) {
  33. window.requestAnimationFrame(() => {
  34. if (inputRef.current) {
  35. inputRef.current.focus()
  36. inputRef.current.setSelectionRange(
  37. 0,
  38. inputRef.current.value.lastIndexOf('.')
  39. )
  40. }
  41. })
  42. }
  43. }, [focusName])
  44. return (
  45. <OLFormGroup controlId="new-doc-name" className={classes.formGroup}>
  46. <OLFormLabel>{label || t('file_name')}</OLFormLabel>
  47. <OLFormControl
  48. type="text"
  49. placeholder={placeholder || t('file_name')}
  50. required
  51. value={name}
  52. onChange={event => setName(event.target.value)}
  53. ref={inputRef}
  54. disabled={inFlight}
  55. />
  56. {touchedName && !validName && (
  57. <OLNotification
  58. type="error"
  59. className="row-spaced-small"
  60. content={t('files_cannot_include_invalid_characters')}
  61. />
  62. )}
  63. {error && <ErrorMessage error={error} />}
  64. </OLFormGroup>
  65. )
  66. }
  67. FileTreeCreateNameInput.propTypes = {
  68. focusName: PropTypes.bool,
  69. label: PropTypes.string,
  70. classes: PropTypes.shape({
  71. formGroup: PropTypes.string,
  72. }),
  73. placeholder: PropTypes.string,
  74. error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
  75. inFlight: PropTypes.bool.isRequired,
  76. }
  77. function ErrorMessage({ error }) {
  78. const { t } = useTranslation()
  79. // if (typeof error === 'string') {
  80. // return error
  81. // }
  82. switch (error.constructor) {
  83. case DuplicateFilenameError:
  84. return (
  85. <OLNotification
  86. type="error"
  87. className="row-spaced-small"
  88. content={t('file_already_exists')}
  89. />
  90. )
  91. case InvalidFilenameError:
  92. return (
  93. <OLNotification
  94. type="error"
  95. className="row-spaced-small"
  96. content={t('files_cannot_include_invalid_characters')}
  97. />
  98. )
  99. case BlockedFilenameError:
  100. return (
  101. <OLNotification
  102. type="error"
  103. className="row-spaced-small"
  104. content={t('blocked_filename')}
  105. />
  106. )
  107. default:
  108. // return <Trans i18nKey="generic_something_went_wrong" />
  109. return null // other errors are displayed elsewhere
  110. }
  111. }
  112. ErrorMessage.propTypes = {
  113. error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
  114. }