file-tree-modal-create-folder.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { useState } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { Button, Modal } from 'react-bootstrap'
  4. import { useTranslation } from 'react-i18next'
  5. import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-focus'
  6. import AccessibleModal from '../../../../shared/components/accessible-modal'
  7. import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
  8. import { DuplicateFilenameError } from '../../errors'
  9. import { isCleanFilename } from '../../util/safe-path'
  10. function FileTreeModalCreateFolder() {
  11. const { t } = useTranslation()
  12. const [name, setName] = useState('')
  13. const [validName, setValidName] = useState(true)
  14. const { isCreatingFolder, inFlight, finishCreatingFolder, cancel, error } =
  15. useFileTreeActionable()
  16. if (!isCreatingFolder) return null // the modal will not be rendered; return early
  17. function handleHide() {
  18. cancel()
  19. }
  20. function handleCreateFolder() {
  21. finishCreatingFolder(name)
  22. }
  23. function errorMessage() {
  24. switch (error.constructor) {
  25. case DuplicateFilenameError:
  26. return t('file_already_exists')
  27. default:
  28. return t('generic_something_went_wrong')
  29. }
  30. }
  31. return (
  32. <AccessibleModal show onHide={handleHide}>
  33. <Modal.Header>
  34. <Modal.Title>{t('new_folder')}</Modal.Title>
  35. </Modal.Header>
  36. <Modal.Body>
  37. <InputName
  38. name={name}
  39. setName={setName}
  40. validName={validName}
  41. setValidName={setValidName}
  42. handleCreateFolder={handleCreateFolder}
  43. />
  44. {!validName ? (
  45. <div
  46. role="alert"
  47. aria-label={t('files_cannot_include_invalid_characters')}
  48. className="alert alert-danger file-tree-modal-alert"
  49. >
  50. {t('files_cannot_include_invalid_characters')}
  51. </div>
  52. ) : null}
  53. {error ? (
  54. <div
  55. role="alert"
  56. aria-label={errorMessage()}
  57. className="alert alert-danger file-tree-modal-alert"
  58. >
  59. {errorMessage()}
  60. </div>
  61. ) : null}
  62. </Modal.Body>
  63. <Modal.Footer>
  64. {inFlight ? (
  65. <Button bsStyle="primary" disabled>
  66. {t('creating')}…
  67. </Button>
  68. ) : (
  69. <>
  70. <Button
  71. bsStyle={null}
  72. className="btn-secondary"
  73. onClick={handleHide}
  74. >
  75. {t('cancel')}
  76. </Button>
  77. <Button
  78. bsStyle="primary"
  79. onClick={handleCreateFolder}
  80. disabled={!validName}
  81. >
  82. {t('create')}
  83. </Button>
  84. </>
  85. )}
  86. </Modal.Footer>
  87. </AccessibleModal>
  88. )
  89. }
  90. function InputName({
  91. name,
  92. setName,
  93. validName,
  94. setValidName,
  95. handleCreateFolder,
  96. }) {
  97. const { autoFocusedRef } = useRefWithAutoFocus()
  98. function handleFocus(ev) {
  99. ev.target.setSelectionRange(0, -1)
  100. }
  101. function handleChange(ev) {
  102. setValidName(isCleanFilename(ev.target.value.trim()))
  103. setName(ev.target.value)
  104. }
  105. function handleKeyDown(ev) {
  106. if (ev.key === 'Enter' && validName) {
  107. handleCreateFolder()
  108. }
  109. }
  110. return (
  111. <input
  112. ref={autoFocusedRef}
  113. className="form-control"
  114. type="text"
  115. value={name}
  116. onKeyDown={handleKeyDown}
  117. onChange={handleChange}
  118. onFocus={handleFocus}
  119. />
  120. )
  121. }
  122. InputName.propTypes = {
  123. name: PropTypes.string.isRequired,
  124. setName: PropTypes.func.isRequired,
  125. validName: PropTypes.bool.isRequired,
  126. setValidName: PropTypes.func.isRequired,
  127. handleCreateFolder: PropTypes.func.isRequired,
  128. }
  129. export default FileTreeModalCreateFolder