outline-pane.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import React, { useState, useEffect } from 'react'
  2. import PropTypes from 'prop-types'
  3. import classNames from 'classnames'
  4. import { useTranslation } from 'react-i18next'
  5. import OutlineRoot from './outline-root'
  6. import Icon from '../../../shared/components/icon'
  7. import localStorage from '../../../infrastructure/local-storage'
  8. import withErrorBoundary from '../../../infrastructure/error-boundary'
  9. import { useProjectContext } from '../../../shared/context/project-context'
  10. const OutlinePane = React.memo(function OutlinePane({
  11. isTexFile,
  12. outline,
  13. jumpToLine,
  14. onToggle,
  15. eventTracking,
  16. highlightedLine,
  17. }) {
  18. const { t } = useTranslation()
  19. const { _id: projectId } = useProjectContext({
  20. _id: PropTypes.string.isRequired,
  21. })
  22. const storageKey = `file_outline.expanded.${projectId}`
  23. const [expanded, setExpanded] = useState(() => {
  24. const storedExpandedState = localStorage.getItem(storageKey) !== false
  25. return storedExpandedState
  26. })
  27. const isOpen = isTexFile && expanded
  28. useEffect(() => {
  29. onToggle(isOpen)
  30. }, [isOpen, onToggle])
  31. const headerClasses = classNames('outline-pane', {
  32. 'outline-pane-disabled': !isTexFile,
  33. })
  34. function handleExpandCollapseClick() {
  35. if (isTexFile) {
  36. localStorage.setItem(storageKey, !expanded)
  37. eventTracking.sendMB(expanded ? 'outline-collapse' : 'outline-expand')
  38. setExpanded(!expanded)
  39. }
  40. }
  41. return (
  42. <div className={headerClasses}>
  43. <header className="outline-header">
  44. <button
  45. className="outline-header-expand-collapse-btn"
  46. disabled={!isTexFile}
  47. onClick={handleExpandCollapseClick}
  48. aria-label={expanded ? t('hide_outline') : t('show_outline')}
  49. >
  50. <Icon
  51. type={isOpen ? 'angle-down' : 'angle-right'}
  52. className="outline-caret-icon"
  53. />
  54. <h4 className="outline-header-name">{t('file_outline')}</h4>
  55. </button>
  56. </header>
  57. {expanded && isTexFile ? (
  58. <div className="outline-body">
  59. <OutlineRoot
  60. outline={outline}
  61. jumpToLine={jumpToLine}
  62. highlightedLine={highlightedLine}
  63. />
  64. </div>
  65. ) : null}
  66. </div>
  67. )
  68. })
  69. OutlinePane.propTypes = {
  70. isTexFile: PropTypes.bool.isRequired,
  71. outline: PropTypes.array.isRequired,
  72. jumpToLine: PropTypes.func.isRequired,
  73. onToggle: PropTypes.func.isRequired,
  74. eventTracking: PropTypes.object.isRequired,
  75. highlightedLine: PropTypes.number,
  76. }
  77. export default withErrorBoundary(OutlinePane)