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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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 React, { useCallback } from 'react'
  5. import { Trans } 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 = 'File Name',
  19. focusName = false,
  20. classes = {},
  21. placeholder = 'File Name',
  22. error
  23. }) {
  24. // the value is stored in a context provider, so it's available elsewhere in the form
  25. const { name, setName, touchedName, validName } = useFileTreeCreateName()
  26. // focus the first part of the filename if needed
  27. const inputRef = useCallback(
  28. element => {
  29. if (element && focusName) {
  30. window.requestAnimationFrame(() => {
  31. element.focus()
  32. element.setSelectionRange(0, element.value.lastIndexOf('.'))
  33. })
  34. }
  35. },
  36. [focusName]
  37. )
  38. return (
  39. <FormGroup controlId="new-doc-name" className={classes.formGroup}>
  40. <ControlLabel>{label}</ControlLabel>
  41. <FormControl
  42. type="text"
  43. placeholder={placeholder}
  44. required
  45. value={name}
  46. onChange={event => setName(event.target.value)}
  47. inputRef={inputRef}
  48. />
  49. <FormControl.Feedback />
  50. {touchedName && !validName && (
  51. <Alert bsStyle="danger" className="row-spaced-small">
  52. <Trans i18nKey="files_cannot_include_invalid_characters" />
  53. </Alert>
  54. )}
  55. {error && <ErrorMessage error={error} />}
  56. </FormGroup>
  57. )
  58. }
  59. FileTreeCreateNameInput.propTypes = {
  60. focusName: PropTypes.bool,
  61. label: PropTypes.string,
  62. classes: PropTypes.shape({
  63. formGroup: PropTypes.string
  64. }),
  65. placeholder: PropTypes.string,
  66. error: PropTypes.oneOfType([PropTypes.object, PropTypes.string])
  67. }
  68. function ErrorMessage({ error }) {
  69. // if (typeof error === 'string') {
  70. // return error
  71. // }
  72. switch (error.constructor) {
  73. case DuplicateFilenameError:
  74. return (
  75. <Alert bsStyle="danger" className="row-spaced-small">
  76. <Trans i18nKey="file_already_exists" />
  77. </Alert>
  78. )
  79. case InvalidFilenameError:
  80. return (
  81. <Alert bsStyle="danger" className="row-spaced-small">
  82. <Trans i18nKey="files_cannot_include_invalid_characters" />
  83. </Alert>
  84. )
  85. case BlockedFilenameError:
  86. return (
  87. <Alert bsStyle="danger" className="row-spaced-small">
  88. <Trans i18nKey="blocked_filename" />
  89. </Alert>
  90. )
  91. default:
  92. // return <Trans i18nKey="generic_something_went_wrong" />
  93. return null // other errors are displayed elsewhere
  94. }
  95. }
  96. ErrorMessage.propTypes = {
  97. error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired
  98. }