mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #10397 from overleaf/td-memoize-file-outline
Memoize file outline GitOrigin-RevId: cb086bab2b6ead251362180d776e7eaff18fc639
This commit is contained in:
parent
6ae22ff596
commit
81e2265e72
4 changed files with 57 additions and 21 deletions
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef, memo } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
|
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
@ -6,15 +6,13 @@ import { useTranslation } from 'react-i18next'
|
||||||
import OutlineList from './outline-list'
|
import OutlineList from './outline-list'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
|
|
||||||
function getChildrenLines(children) {
|
const OutlineItem = memo(function OutlineItem({
|
||||||
return (children || [])
|
outlineItem,
|
||||||
.map(child => {
|
jumpToLine,
|
||||||
return getChildrenLines(child.children).concat(child.line)
|
highlightedLine,
|
||||||
})
|
matchesHighlightedLine,
|
||||||
.flat()
|
containsHighlightedLine,
|
||||||
}
|
}) {
|
||||||
|
|
||||||
function OutlineItem({ outlineItem, jumpToLine, highlightedLine }) {
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState(true)
|
const [expanded, setExpanded] = useState(true)
|
||||||
|
@ -25,12 +23,8 @@ function OutlineItem({ outlineItem, jumpToLine, highlightedLine }) {
|
||||||
'outline-item-no-children': !outlineItem.children,
|
'outline-item-no-children': !outlineItem.children,
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasHighlightedChild =
|
const hasHighlightedChild = !expanded && containsHighlightedLine
|
||||||
!expanded &&
|
const isHighlighted = matchesHighlightedLine || hasHighlightedChild
|
||||||
getChildrenLines(outlineItem.children).includes(highlightedLine)
|
|
||||||
|
|
||||||
const isHighlighted =
|
|
||||||
highlightedLine === outlineItem.line || hasHighlightedChild
|
|
||||||
|
|
||||||
const itemLinkClasses = classNames('outline-item-link', {
|
const itemLinkClasses = classNames('outline-item-link', {
|
||||||
'outline-item-link-highlight': isHighlighted,
|
'outline-item-link-highlight': isHighlighted,
|
||||||
|
@ -90,16 +84,21 @@ function OutlineItem({ outlineItem, jumpToLine, highlightedLine }) {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{expanded && outlineItem.children ? (
|
{expanded && outlineItem.children ? (
|
||||||
|
// highlightedLine is only provided to this list if the list contains
|
||||||
|
// the highlighted line. This means that whenever the list does not
|
||||||
|
// contain the highlighted line, the props provided to it are the same
|
||||||
|
// and the component can be memoized.
|
||||||
<OutlineList
|
<OutlineList
|
||||||
outline={outlineItem.children}
|
outline={outlineItem.children}
|
||||||
jumpToLine={jumpToLine}
|
jumpToLine={jumpToLine}
|
||||||
isRoot={false}
|
isRoot={false}
|
||||||
highlightedLine={highlightedLine}
|
highlightedLine={containsHighlightedLine ? highlightedLine : null}
|
||||||
|
containsHighlightedLine={containsHighlightedLine}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
OutlineItem.propTypes = {
|
OutlineItem.propTypes = {
|
||||||
outlineItem: PropTypes.exact({
|
outlineItem: PropTypes.exact({
|
||||||
|
@ -113,6 +112,8 @@ OutlineItem.propTypes = {
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
jumpToLine: PropTypes.func.isRequired,
|
jumpToLine: PropTypes.func.isRequired,
|
||||||
highlightedLine: PropTypes.number,
|
highlightedLine: PropTypes.number,
|
||||||
|
matchesHighlightedLine: PropTypes.bool,
|
||||||
|
containsHighlightedLine: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OutlineItem
|
export default OutlineItem
|
||||||
|
|
|
@ -1,32 +1,64 @@
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import OutlineItem from './outline-item'
|
import OutlineItem from './outline-item'
|
||||||
|
import { memo } from 'react'
|
||||||
|
|
||||||
function OutlineList({ outline, jumpToLine, isRoot, highlightedLine }) {
|
function getChildrenLines(children) {
|
||||||
|
return (children || [])
|
||||||
|
.map(child => {
|
||||||
|
return getChildrenLines(child.children).concat(child.line)
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutlineList = memo(function OutlineList({
|
||||||
|
outline,
|
||||||
|
jumpToLine,
|
||||||
|
isRoot,
|
||||||
|
highlightedLine,
|
||||||
|
containsHighlightedLine,
|
||||||
|
}) {
|
||||||
const listClasses = classNames('outline-item-list', {
|
const listClasses = classNames('outline-item-list', {
|
||||||
'outline-item-list-root': isRoot,
|
'outline-item-list-root': isRoot,
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<ul className={listClasses} role={isRoot ? 'tree' : 'group'}>
|
<ul className={listClasses} role={isRoot ? 'tree' : 'group'}>
|
||||||
{outline.map((outlineItem, idx) => {
|
{outline.map((outlineItem, idx) => {
|
||||||
|
const matchesHighlightedLine =
|
||||||
|
containsHighlightedLine && highlightedLine === outlineItem.line
|
||||||
|
const itemContainsHighlightedLine =
|
||||||
|
containsHighlightedLine &&
|
||||||
|
getChildrenLines(outlineItem.children).includes(highlightedLine)
|
||||||
|
|
||||||
|
// highlightedLine is only provided to the item if the item matches or
|
||||||
|
// contains the highlighted line. This means that whenever the item does
|
||||||
|
// not contain the highlighted line, the props provided to it are the
|
||||||
|
// same and the component can be memoized.
|
||||||
return (
|
return (
|
||||||
<OutlineItem
|
<OutlineItem
|
||||||
key={`${outlineItem.level}-${idx}`}
|
key={`${outlineItem.level}-${idx}`}
|
||||||
outlineItem={outlineItem}
|
outlineItem={outlineItem}
|
||||||
jumpToLine={jumpToLine}
|
jumpToLine={jumpToLine}
|
||||||
highlightedLine={highlightedLine}
|
highlightedLine={
|
||||||
|
matchesHighlightedLine || itemContainsHighlightedLine
|
||||||
|
? highlightedLine
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
matchesHighlightedLine={matchesHighlightedLine}
|
||||||
|
containsHighlightedLine={itemContainsHighlightedLine}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
OutlineList.propTypes = {
|
OutlineList.propTypes = {
|
||||||
outline: PropTypes.array.isRequired,
|
outline: PropTypes.array.isRequired,
|
||||||
jumpToLine: PropTypes.func.isRequired,
|
jumpToLine: PropTypes.func.isRequired,
|
||||||
isRoot: PropTypes.bool,
|
isRoot: PropTypes.bool,
|
||||||
highlightedLine: PropTypes.number,
|
highlightedLine: PropTypes.number,
|
||||||
|
containsHighlightedLine: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OutlineList
|
export default OutlineList
|
||||||
|
|
|
@ -14,6 +14,7 @@ function OutlineRoot({ outline, jumpToLine, highlightedLine }) {
|
||||||
jumpToLine={jumpToLine}
|
jumpToLine={jumpToLine}
|
||||||
isRoot
|
isRoot
|
||||||
highlightedLine={highlightedLine}
|
highlightedLine={highlightedLine}
|
||||||
|
containsHighlightedLine
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="outline-body-no-elements">
|
<div className="outline-body-no-elements">
|
||||||
|
|
|
@ -54,6 +54,7 @@ describe('<OutlineItem />', function () {
|
||||||
outlineItem={outlineItem}
|
outlineItem={outlineItem}
|
||||||
jumpToLine={jumpToLine}
|
jumpToLine={jumpToLine}
|
||||||
highlightedLine={1}
|
highlightedLine={1}
|
||||||
|
matchesHighlightedLine
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ describe('<OutlineItem />', function () {
|
||||||
outlineItem={outlineItem}
|
outlineItem={outlineItem}
|
||||||
jumpToLine={jumpToLine}
|
jumpToLine={jumpToLine}
|
||||||
highlightedLine={2}
|
highlightedLine={2}
|
||||||
|
containsHighlightedLine
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue