file-tree-create-name-input.js 3.1 KB

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