file-name-input.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { useCallback, useEffect, useState } from 'react'
  2. import { File, FileOrDirectory } from '../../utils/file'
  3. import { useTranslation } from 'react-i18next'
  4. import { useCurrentProjectFolders } from '@/features/source-editor/hooks/use-current-project-folders'
  5. import OLFormControl from '@/features/ui/components/ol/ol-form-control'
  6. import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
  7. import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
  8. import OLNotification from '@/features/ui/components/ol/ol-notification'
  9. type FileNameInputProps = Omit<
  10. React.ComponentProps<typeof OLFormControl>,
  11. 'onFocus'
  12. > & { targetFolder: File | null; label: string }
  13. function findFile(
  14. folder: { id: string; name: string },
  15. project: FileOrDirectory
  16. ): FileOrDirectory | null {
  17. if (project.id === folder.id) {
  18. return project
  19. }
  20. if (project.type !== 'folder') {
  21. return null
  22. }
  23. for (const child of project.children ?? []) {
  24. const search = findFile(folder, child)
  25. if (search) {
  26. return search
  27. }
  28. }
  29. return null
  30. }
  31. function hasOverlap(
  32. name: string,
  33. folder: { id: string; name: string },
  34. project: FileOrDirectory
  35. ): boolean {
  36. const directory = findFile(folder, project)
  37. if (!directory) {
  38. return false
  39. }
  40. for (const child of directory.children ?? []) {
  41. if (child.name === name) {
  42. return true
  43. }
  44. }
  45. return false
  46. }
  47. export const FileNameInput = ({
  48. id,
  49. label,
  50. targetFolder,
  51. ...props
  52. }: FileNameInputProps) => {
  53. const { t } = useTranslation()
  54. const [overlap, setOverlap] = useState<boolean>(false)
  55. const { rootFolder } = useCurrentProjectFolders()
  56. const { value } = props
  57. useEffect(() => {
  58. if (value) {
  59. setOverlap(
  60. hasOverlap(String(value), targetFolder ?? rootFolder, rootFolder)
  61. )
  62. } else {
  63. setOverlap(false)
  64. }
  65. }, [value, targetFolder, rootFolder])
  66. const onFocus = useCallback((event: React.FocusEvent<HTMLInputElement>) => {
  67. if (!event.target) {
  68. return true
  69. }
  70. const fileName = event.target.value
  71. const fileExtensionIndex = fileName.lastIndexOf('.')
  72. if (fileExtensionIndex >= 0) {
  73. event.target.setSelectionRange(0, fileExtensionIndex)
  74. }
  75. }, [])
  76. return (
  77. <>
  78. <OLFormGroup controlId={id}>
  79. <OLFormLabel>{label}</OLFormLabel>
  80. <OLFormControl onFocus={onFocus} {...props} />
  81. {overlap && (
  82. <OLNotification
  83. type="warning"
  84. content={t(
  85. 'a_file_with_that_name_already_exists_and_will_be_overriden'
  86. )}
  87. className="mt-1 mb-0"
  88. />
  89. )}
  90. </OLFormGroup>
  91. </>
  92. )
  93. }