mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Memoize FileTree and outline toggle button components (#16776)
GitOrigin-RevId: 299ed9d568650ce37edba87643112d1cd6d12fd4
This commit is contained in:
parent
f06f2ef99e
commit
c443322a41
8 changed files with 93 additions and 58 deletions
|
@ -176,7 +176,10 @@ export const ChatContext = createContext<
|
|||
>(undefined)
|
||||
|
||||
export const ChatProvider: FC = ({ children }) => {
|
||||
const clientId = useRef(uuid())
|
||||
const clientId = useRef<string>()
|
||||
if (clientId.current === undefined) {
|
||||
clientId.current = uuid()
|
||||
}
|
||||
const user = useUserContext()
|
||||
const { _id: projectId } = useProjectContext()
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useState } from 'react'
|
||||
import React, { memo, useCallback, useState } from 'react'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import { useReferencesContext } from '@/features/ide-react/context/references-context'
|
||||
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||
|
@ -7,7 +7,7 @@ import { RefProviders } from '../../../../../types/user'
|
|||
import FileTreeRoot from '@/features/file-tree/components/file-tree-root'
|
||||
import { useFileTreeOpenContext } from '@/features/ide-react/context/file-tree-open-context'
|
||||
|
||||
export function FileTree() {
|
||||
export const FileTree = memo(function FileTree() {
|
||||
const user = useUserContext()
|
||||
const { indexAllReferences } = useReferencesContext()
|
||||
const { setStartedFreeTrial } = useIdeReactContext()
|
||||
|
@ -44,4 +44,4 @@ export function FileTree() {
|
|||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { memo, type Dispatch, type SetStateAction } from 'react'
|
||||
import Icon from '@/shared/components/icon'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const OutlineItemToggleButton = memo<{
|
||||
expanded: boolean
|
||||
setExpanded: Dispatch<SetStateAction<boolean>>
|
||||
}>(({ expanded, setExpanded }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<button
|
||||
className="outline-item-expand-collapse-btn"
|
||||
onClick={() => setExpanded(value => !value)}
|
||||
aria-label={expanded ? t('collapse') : t('expand')}
|
||||
>
|
||||
<Icon
|
||||
type={expanded ? 'angle-down' : 'angle-right'}
|
||||
className="outline-caret-icon"
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
OutlineItemToggleButton.displayName = 'OutlineItemToggleButton'
|
|
@ -2,9 +2,8 @@ import { useState, useEffect, useRef, memo } from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
|
||||
import classNames from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import OutlineList from './outline-list'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import { OutlineItemToggleButton } from '@/features/outline/components/outline-item-toggle-button'
|
||||
|
||||
const OutlineItem = memo(function OutlineItem({
|
||||
outlineItem,
|
||||
|
@ -13,8 +12,6 @@ const OutlineItem = memo(function OutlineItem({
|
|||
matchesHighlightedLine,
|
||||
containsHighlightedLine,
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [expanded, setExpanded] = useState(true)
|
||||
const titleElementRef = useRef()
|
||||
const isHighlightedRef = useRef(false)
|
||||
|
@ -30,10 +27,6 @@ const OutlineItem = memo(function OutlineItem({
|
|||
'outline-item-link-highlight': isHighlighted,
|
||||
})
|
||||
|
||||
function handleExpandCollapseClick() {
|
||||
setExpanded(!expanded)
|
||||
}
|
||||
|
||||
function handleOutlineItemLinkClick(event) {
|
||||
const syncToPdf = event.detail === 2 // double-click = sync to PDF
|
||||
jumpToLine(outlineItem.line, syncToPdf)
|
||||
|
@ -65,18 +58,12 @@ const OutlineItem = memo(function OutlineItem({
|
|||
aria-label={outlineItem.title}
|
||||
>
|
||||
<div className="outline-item-row">
|
||||
{outlineItem.children ? (
|
||||
<button
|
||||
className="outline-item-expand-collapse-btn"
|
||||
onClick={handleExpandCollapseClick}
|
||||
aria-label={expanded ? t('collapse') : t('expand')}
|
||||
>
|
||||
<Icon
|
||||
type={expanded ? 'angle-down' : 'angle-right'}
|
||||
className="outline-caret-icon"
|
||||
/>
|
||||
</button>
|
||||
) : null}
|
||||
{!!outlineItem.children && (
|
||||
<OutlineItemToggleButton
|
||||
expanded={expanded}
|
||||
setExpanded={setExpanded}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
className={itemLinkClasses}
|
||||
onClick={handleOutlineItemLinkClick}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import OutlineRoot from './outline-root'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||
import Tooltip from '../../../shared/components/tooltip'
|
||||
import { OutlineToggleButton } from '@/features/outline/components/outline-toggle-button'
|
||||
|
||||
const OutlinePane = React.memo<{
|
||||
isTexFile: boolean
|
||||
|
@ -28,8 +26,6 @@ const OutlinePane = React.memo<{
|
|||
expanded,
|
||||
toggleExpanded,
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isOpen = Boolean(isTexFile && expanded)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -43,32 +39,13 @@ const OutlinePane = React.memo<{
|
|||
return (
|
||||
<div className={headerClasses}>
|
||||
<header className="outline-header">
|
||||
<button
|
||||
className="outline-header-expand-collapse-btn"
|
||||
disabled={!isTexFile}
|
||||
onClick={toggleExpanded}
|
||||
aria-label={expanded ? t('hide_outline') : t('show_outline')}
|
||||
>
|
||||
<Icon
|
||||
type={isOpen ? 'angle-down' : 'angle-right'}
|
||||
className="outline-caret-icon"
|
||||
/>
|
||||
<h4 className="outline-header-name">{t('file_outline')}</h4>
|
||||
{isPartial && (
|
||||
<Tooltip
|
||||
id="partial-outline"
|
||||
description={t('partial_outline_warning')}
|
||||
overlayProps={{ placement: 'top' }}
|
||||
>
|
||||
<span role="status">
|
||||
<Icon
|
||||
type="exclamation-triangle"
|
||||
aria-label={t('partial_outline_warning')}
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</button>
|
||||
<OutlineToggleButton
|
||||
toggleExpanded={toggleExpanded}
|
||||
expanded={expanded}
|
||||
isOpen={isOpen}
|
||||
isPartial={isPartial}
|
||||
isTexFile={isTexFile}
|
||||
/>
|
||||
</header>
|
||||
{isOpen && (
|
||||
<div className="outline-body">
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import Icon from '@/shared/components/icon'
|
||||
import Tooltip from '@/shared/components/tooltip'
|
||||
import React, { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const OutlineToggleButton = memo<{
|
||||
isTexFile: boolean
|
||||
toggleExpanded: () => void
|
||||
expanded?: boolean
|
||||
isOpen: boolean
|
||||
isPartial: boolean
|
||||
}>(({ isTexFile, toggleExpanded, expanded, isOpen, isPartial }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<button
|
||||
className="outline-header-expand-collapse-btn"
|
||||
disabled={!isTexFile}
|
||||
onClick={toggleExpanded}
|
||||
aria-label={expanded ? t('hide_outline') : t('show_outline')}
|
||||
>
|
||||
<Icon
|
||||
type={isOpen ? 'angle-down' : 'angle-right'}
|
||||
className="outline-caret-icon"
|
||||
/>
|
||||
<h4 className="outline-header-name">{t('file_outline')}</h4>
|
||||
{isPartial && (
|
||||
<Tooltip
|
||||
id="partial-outline"
|
||||
description={t('partial_outline_warning')}
|
||||
overlayProps={{ placement: 'top' }}
|
||||
>
|
||||
<span role="status">
|
||||
<Icon
|
||||
type="exclamation-triangle"
|
||||
aria-label={t('partial_outline_warning')}
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
})
|
||||
OutlineToggleButton.displayName = 'OutlineToggleButton'
|
|
@ -1,11 +1,11 @@
|
|||
import { createContext, FC, useContext } from 'react'
|
||||
import { createContext, FC, useContext, useMemo } from 'react'
|
||||
import getMeta from '../../utils/meta'
|
||||
import { User } from '../../../../types/user'
|
||||
|
||||
export const UserContext = createContext<User | undefined>(undefined)
|
||||
|
||||
export const UserProvider: FC = ({ children }) => {
|
||||
const user = getMeta('ol-user')
|
||||
const user = useMemo(() => getMeta('ol-user'), [])
|
||||
|
||||
return <UserContext.Provider value={user}>{children}</UserContext.Provider>
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ function setTitle(title: string) {
|
|||
}
|
||||
|
||||
function useBrowserWindow() {
|
||||
const [hasFocus, setHasFocus] = useState(document.hasFocus())
|
||||
const [hasFocus, setHasFocus] = useState(() => document.hasFocus())
|
||||
|
||||
useEffect(() => {
|
||||
function handleFocusEvent() {
|
||||
|
|
Loading…
Reference in a new issue