file-tree-folder.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { useEffect } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { useTranslation } from 'react-i18next'
  4. import classNames from 'classnames'
  5. import Icon from '../../../shared/components/icon'
  6. import {
  7. useFileTreeSelectable,
  8. useSelectableEntity,
  9. } from '../contexts/file-tree-selectable'
  10. import { useDroppable } from '../contexts/file-tree-draggable'
  11. import FileTreeItemInner from './file-tree-item/file-tree-item-inner'
  12. import FileTreeFolderList from './file-tree-folder-list'
  13. import usePersistedState from '../../../shared/hooks/use-persisted-state'
  14. function FileTreeFolder({ name, id, folders, docs, files }) {
  15. const { t } = useTranslation()
  16. const { isSelected, props: selectableEntityProps } = useSelectableEntity(id)
  17. const { selectedEntityParentIds } = useFileTreeSelectable(id)
  18. const [expanded, setExpanded] = usePersistedState(
  19. `folder.${id}.expanded`,
  20. false
  21. )
  22. useEffect(() => {
  23. if (selectedEntityParentIds.has(id)) {
  24. setExpanded(true)
  25. }
  26. }, [id, selectedEntityParentIds, setExpanded])
  27. function handleExpandCollapseClick() {
  28. setExpanded(!expanded)
  29. }
  30. const { isOver: isOverRoot, dropRef: dropRefRoot } = useDroppable(id)
  31. const { isOver: isOverList, dropRef: dropRefList } = useDroppable(id)
  32. const icons = (
  33. <>
  34. <button
  35. onClick={handleExpandCollapseClick}
  36. aria-label={expanded ? t('collapse') : t('expand')}
  37. >
  38. <Icon type={expanded ? 'angle-down' : 'angle-right'} modifier="fw" />
  39. </button>
  40. <Icon type={expanded ? 'folder-open' : 'folder'} modifier="fw" />
  41. </>
  42. )
  43. return (
  44. <>
  45. <li
  46. role="treeitem"
  47. {...selectableEntityProps}
  48. aria-expanded={expanded}
  49. aria-label={name}
  50. tabIndex="0"
  51. ref={dropRefRoot}
  52. className={classNames(selectableEntityProps.className, {
  53. 'dnd-droppable-hover': isOverRoot || isOverList,
  54. })}
  55. >
  56. <FileTreeItemInner
  57. id={id}
  58. name={name}
  59. isSelected={isSelected}
  60. icons={icons}
  61. />
  62. </li>
  63. {expanded ? (
  64. <FileTreeFolderList
  65. folders={folders}
  66. docs={docs}
  67. files={files}
  68. dropRef={dropRefList}
  69. />
  70. ) : null}
  71. </>
  72. )
  73. }
  74. FileTreeFolder.propTypes = {
  75. name: PropTypes.string.isRequired,
  76. id: PropTypes.string.isRequired,
  77. folders: PropTypes.array.isRequired,
  78. docs: PropTypes.array.isRequired,
  79. files: PropTypes.array.isRequired,
  80. }
  81. export default FileTreeFolder