file-tree-folder.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import { useCallback, useEffect } from 'react'
  2. import classNames from 'classnames'
  3. import {
  4. useFileTreeSelectable,
  5. useSelectableEntity,
  6. } from '../contexts/file-tree-selectable'
  7. import { useDroppable } from '../contexts/file-tree-draggable'
  8. import FileTreeItemInner from './file-tree-item/file-tree-item-inner'
  9. import FileTreeFolderList from './file-tree-folder-list'
  10. import usePersistedState from '../../../shared/hooks/use-persisted-state'
  11. import { Folder } from '../../../../../types/folder'
  12. import { Doc } from '../../../../../types/doc'
  13. import { FileRef } from '../../../../../types/file-ref'
  14. import FileTreeFolderIcons from './file-tree-folder-icons'
  15. import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
  16. function FileTreeFolder({
  17. name,
  18. id,
  19. folders,
  20. docs,
  21. files,
  22. }: {
  23. name: string
  24. id: string
  25. folders: Folder[]
  26. docs: Doc[]
  27. files: FileRef[]
  28. }) {
  29. const newEditor = useIsNewEditorEnabled()
  30. const { isSelected, props: selectableEntityProps } = useSelectableEntity(
  31. id,
  32. 'folder'
  33. )
  34. const { selectedEntityParentIds } = useFileTreeSelectable()
  35. const [expanded, setExpanded] = usePersistedState(
  36. `folder.${id}.expanded`,
  37. false
  38. )
  39. useEffect(() => {
  40. if (selectedEntityParentIds.has(id)) {
  41. setExpanded(true)
  42. }
  43. }, [id, selectedEntityParentIds, setExpanded])
  44. const handleExpandCollapseClick = useCallback(() => {
  45. setExpanded(expanded => !expanded)
  46. }, [setExpanded])
  47. const onClick = useCallback(() => {
  48. if (newEditor) {
  49. handleExpandCollapseClick()
  50. }
  51. }, [newEditor, handleExpandCollapseClick])
  52. const { isOver: isOverRoot, dropRef: dropRefRoot } = useDroppable(id)
  53. const { isOver: isOverList, dropRef: dropRefList } = useDroppable(id)
  54. return (
  55. <>
  56. <li
  57. // eslint-disable-next-line jsx-a11y/role-has-required-aria-props
  58. role="treeitem"
  59. // aria-selected is provided in selectableEntityProps
  60. {...selectableEntityProps}
  61. aria-expanded={expanded}
  62. aria-label={name}
  63. ref={dropRefRoot}
  64. className={classNames(selectableEntityProps.className, {
  65. 'dnd-droppable-hover': isOverRoot || isOverList,
  66. })}
  67. translate="no"
  68. >
  69. <FileTreeItemInner
  70. id={id}
  71. name={name}
  72. type="folder"
  73. isSelected={isSelected}
  74. onClick={onClick}
  75. icons={
  76. <FileTreeFolderIcons
  77. expanded={expanded}
  78. onExpandCollapseClick={handleExpandCollapseClick}
  79. />
  80. }
  81. />
  82. </li>
  83. {expanded ? (
  84. <FileTreeFolderList
  85. folders={folders}
  86. docs={docs}
  87. files={files}
  88. dropRef={dropRefList}
  89. />
  90. ) : null}
  91. </>
  92. )
  93. }
  94. export default FileTreeFolder