mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
Memoize some editor React components (#28963)
GitOrigin-RevId: 6440df9ac3ef1bf1839dff07eb2f55b52b581d3e
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import MaterialIcon, { IconProps } from '@/shared/components/material-icon'
|
||||
|
||||
export const LinkedFileIcon = (
|
||||
props: Omit<IconProps, 'type' | 'modifier' | 'className' | 'unfilled'>
|
||||
props: Omit<IconProps, 'type' | 'modifier' | 'className' | 'unfilled' | 'ref'>
|
||||
) => {
|
||||
return (
|
||||
<MaterialIcon
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { memo } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import HotkeysModalBottomText from './hotkeys-modal-bottom-text'
|
||||
import {
|
||||
@@ -11,7 +12,7 @@ import OLButton from '@/shared/components/ol/ol-button'
|
||||
import OLRow from '@/shared/components/ol/ol-row'
|
||||
import OLCol from '@/shared/components/ol/ol-col'
|
||||
|
||||
export default function HotkeysModal({
|
||||
export default memo(function HotkeysModal({
|
||||
animation = true,
|
||||
handleHide,
|
||||
show,
|
||||
@@ -205,7 +206,7 @@ export default function HotkeysModal({
|
||||
</OLModalFooter>
|
||||
</OLModal>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function Hotkey({
|
||||
combination,
|
||||
|
||||
@@ -7,20 +7,20 @@ import { OLToast } from '@/shared/components/ol/ol-toast'
|
||||
import { OLToastContainer } from '@/shared/components/ol/ol-toast-container'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
import useTutorial from '@/shared/hooks/promotions/use-tutorial'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type EditorSurveyPage = 'ease-of-use' | 'meets-my-needs' | 'thank-you'
|
||||
|
||||
export default function EditorSurvey() {
|
||||
export default memo(function EditorSurvey() {
|
||||
return (
|
||||
<OLToastContainer className="editor-survey-toast">
|
||||
<EditorSurveyContent />
|
||||
</OLToastContainer>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const TUTORIAL_KEY = 'editor-popup-ux-survey'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OLToast, OLToastProps } from '@/shared/components/ol/ol-toast'
|
||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import { Fragment, ReactElement, useCallback, useState } from 'react'
|
||||
import { Fragment, memo, ReactElement, useCallback, useState } from 'react'
|
||||
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||
@@ -30,7 +30,7 @@ const GENERATOR_MAP: Map<string, GlobalToastGenerator> = new Map(
|
||||
|
||||
let toastCounter = 1
|
||||
|
||||
export const GlobalToasts = () => {
|
||||
export const GlobalToasts = memo(function GlobalToasts() {
|
||||
const [toasts, setToasts] = useState<
|
||||
{ component: ReactElement; id: string }[]
|
||||
>([])
|
||||
@@ -96,4 +96,4 @@ export const GlobalToasts = () => {
|
||||
))}
|
||||
</OLToastContainer>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
CSSProperties,
|
||||
FC,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
@@ -94,166 +95,168 @@ const ReviewTooltipMenu: FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const ReviewTooltipMenuContent: FC<{ onAddComment: () => void }> = ({
|
||||
onAddComment,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const view = useCodeMirrorViewContext()
|
||||
const state = useCodeMirrorStateContext()
|
||||
const { reviewPanelOpen } = useLayoutContext()
|
||||
const ranges = useRangesContext()
|
||||
const { acceptChanges, rejectChanges } = useRangesActionsContext()
|
||||
const { showGenericConfirmModal } = useModalsContext()
|
||||
const { wantTrackChanges } = useEditorPropertiesContext()
|
||||
const [tooltipStyle, setTooltipStyle] = useState<CSSProperties | undefined>()
|
||||
const [visible, setVisible] = useState(false)
|
||||
const ReviewTooltipMenuContent = memo<{ onAddComment: () => void }>(
|
||||
function ReviewTooltipMenuContent({ onAddComment }) {
|
||||
const { t } = useTranslation()
|
||||
const view = useCodeMirrorViewContext()
|
||||
const state = useCodeMirrorStateContext()
|
||||
const { reviewPanelOpen } = useLayoutContext()
|
||||
const ranges = useRangesContext()
|
||||
const { acceptChanges, rejectChanges } = useRangesActionsContext()
|
||||
const { showGenericConfirmModal } = useModalsContext()
|
||||
const { wantTrackChanges } = useEditorPropertiesContext()
|
||||
const [tooltipStyle, setTooltipStyle] = useState<
|
||||
CSSProperties | undefined
|
||||
>()
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
const changesInSelection = useMemo(() => {
|
||||
return (ranges?.changes ?? []).filter(({ op }) => {
|
||||
const opFrom = op.p
|
||||
const opLength = isInsertOperation(op) ? op.i.length : 0
|
||||
const opTo = opFrom + opLength
|
||||
const selection = state.selection.main
|
||||
return opFrom >= selection.from && opTo <= selection.to
|
||||
})
|
||||
}, [ranges, state.selection.main])
|
||||
const changesInSelection = useMemo(() => {
|
||||
return (ranges?.changes ?? []).filter(({ op }) => {
|
||||
const opFrom = op.p
|
||||
const opLength = isInsertOperation(op) ? op.i.length : 0
|
||||
const opTo = opFrom + opLength
|
||||
const selection = state.selection.main
|
||||
return opFrom >= selection.from && opTo <= selection.to
|
||||
})
|
||||
}, [ranges, state.selection.main])
|
||||
|
||||
const acceptChangesHandler = useCallback(() => {
|
||||
const nChanges = numberOfChangesInSelection(
|
||||
const acceptChangesHandler = useCallback(() => {
|
||||
const nChanges = numberOfChangesInSelection(
|
||||
ranges,
|
||||
view.state.selection.main
|
||||
)
|
||||
showGenericConfirmModal({
|
||||
message: t('confirm_accept_selected_changes', { count: nChanges }),
|
||||
title: t('accept_selected_changes'),
|
||||
onConfirm: async () => {
|
||||
await acceptChanges(...changesInSelection)
|
||||
},
|
||||
primaryVariant: 'danger',
|
||||
})
|
||||
}, [
|
||||
acceptChanges,
|
||||
changesInSelection,
|
||||
ranges,
|
||||
view.state.selection.main
|
||||
)
|
||||
showGenericConfirmModal({
|
||||
message: t('confirm_accept_selected_changes', { count: nChanges }),
|
||||
title: t('accept_selected_changes'),
|
||||
onConfirm: async () => {
|
||||
await acceptChanges(...changesInSelection)
|
||||
},
|
||||
primaryVariant: 'danger',
|
||||
})
|
||||
}, [
|
||||
acceptChanges,
|
||||
changesInSelection,
|
||||
ranges,
|
||||
showGenericConfirmModal,
|
||||
view,
|
||||
t,
|
||||
])
|
||||
showGenericConfirmModal,
|
||||
view,
|
||||
t,
|
||||
])
|
||||
|
||||
const rejectChangesHandler = useCallback(() => {
|
||||
const nChanges = numberOfChangesInSelection(
|
||||
const rejectChangesHandler = useCallback(() => {
|
||||
const nChanges = numberOfChangesInSelection(
|
||||
ranges,
|
||||
view.state.selection.main
|
||||
)
|
||||
showGenericConfirmModal({
|
||||
message: t('confirm_reject_selected_changes', { count: nChanges }),
|
||||
title: t('reject_selected_changes'),
|
||||
onConfirm: async () => {
|
||||
await rejectChanges(...changesInSelection)
|
||||
},
|
||||
primaryVariant: 'danger',
|
||||
})
|
||||
}, [
|
||||
showGenericConfirmModal,
|
||||
t,
|
||||
ranges,
|
||||
view.state.selection.main
|
||||
)
|
||||
showGenericConfirmModal({
|
||||
message: t('confirm_reject_selected_changes', { count: nChanges }),
|
||||
title: t('reject_selected_changes'),
|
||||
onConfirm: async () => {
|
||||
await rejectChanges(...changesInSelection)
|
||||
},
|
||||
primaryVariant: 'danger',
|
||||
})
|
||||
}, [
|
||||
showGenericConfirmModal,
|
||||
t,
|
||||
ranges,
|
||||
view,
|
||||
rejectChanges,
|
||||
changesInSelection,
|
||||
])
|
||||
view,
|
||||
rejectChanges,
|
||||
changesInSelection,
|
||||
])
|
||||
|
||||
const showChangesButtons = changesInSelection.length > 0
|
||||
const showChangesButtons = changesInSelection.length > 0
|
||||
|
||||
useEffect(() => {
|
||||
view.requestMeasure({
|
||||
key: 'review-tooltip-outside-viewport',
|
||||
read(view) {
|
||||
const cursorCoords = view.coordsAtPos(view.state.selection.main.head)
|
||||
useEffect(() => {
|
||||
view.requestMeasure({
|
||||
key: 'review-tooltip-outside-viewport',
|
||||
read(view) {
|
||||
const cursorCoords = view.coordsAtPos(view.state.selection.main.head)
|
||||
|
||||
if (!cursorCoords) {
|
||||
return
|
||||
}
|
||||
if (!cursorCoords) {
|
||||
return
|
||||
}
|
||||
|
||||
const scrollDomRect = view.scrollDOM.getBoundingClientRect()
|
||||
const contentDomRect = view.contentDOM.getBoundingClientRect()
|
||||
const editorRightPos = contentDomRect.right - CM_LINE_RIGHT_PADDING
|
||||
const scrollDomRect = view.scrollDOM.getBoundingClientRect()
|
||||
const contentDomRect = view.contentDOM.getBoundingClientRect()
|
||||
const editorRightPos = contentDomRect.right - CM_LINE_RIGHT_PADDING
|
||||
|
||||
if (
|
||||
cursorCoords.top > scrollDomRect.top &&
|
||||
cursorCoords.top < scrollDomRect.bottom
|
||||
) {
|
||||
return
|
||||
}
|
||||
if (
|
||||
cursorCoords.top > scrollDomRect.top &&
|
||||
cursorCoords.top < scrollDomRect.bottom
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
position: 'fixed' as const,
|
||||
top: scrollDomRect.top + EDIT_MODE_SWITCH_WIDGET_HEIGHT,
|
||||
right: window.innerWidth - editorRightPos,
|
||||
}
|
||||
},
|
||||
write(res) {
|
||||
setTooltipStyle(res)
|
||||
},
|
||||
})
|
||||
}, [view, reviewPanelOpen, wantTrackChanges])
|
||||
return {
|
||||
position: 'fixed' as const,
|
||||
top: scrollDomRect.top + EDIT_MODE_SWITCH_WIDGET_HEIGHT,
|
||||
right: window.innerWidth - editorRightPos,
|
||||
}
|
||||
},
|
||||
write(res) {
|
||||
setTooltipStyle(res)
|
||||
},
|
||||
})
|
||||
}, [view, reviewPanelOpen, wantTrackChanges])
|
||||
|
||||
useEffect(() => {
|
||||
setVisible(false)
|
||||
const timeout = setTimeout(() => {
|
||||
setVisible(true)
|
||||
}, TOOLTIP_SHOW_DELAY)
|
||||
useEffect(() => {
|
||||
setVisible(false)
|
||||
const timeout = setTimeout(() => {
|
||||
setVisible(true)
|
||||
}, TOOLTIP_SHOW_DELAY)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}, [])
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('review-tooltip-menu', {
|
||||
'review-tooltip-menu-visible': visible,
|
||||
})}
|
||||
style={tooltipStyle}
|
||||
>
|
||||
<button
|
||||
className="review-tooltip-menu-button review-tooltip-add-comment-button"
|
||||
onClick={onAddComment}
|
||||
return (
|
||||
<div
|
||||
className={classNames('review-tooltip-menu', {
|
||||
'review-tooltip-menu-visible': visible,
|
||||
})}
|
||||
style={tooltipStyle}
|
||||
>
|
||||
<MaterialIcon type="chat" />
|
||||
{t('add_comment')}
|
||||
</button>
|
||||
{showChangesButtons && (
|
||||
<>
|
||||
<div className="review-tooltip-menu-divider" />
|
||||
<OLTooltip
|
||||
id="accept-all-changes"
|
||||
description={t('accept_selected_changes')}
|
||||
>
|
||||
<button
|
||||
className="review-tooltip-menu-button"
|
||||
onClick={acceptChangesHandler}
|
||||
aria-label={t('accept_selected_changes')}
|
||||
<button
|
||||
className="review-tooltip-menu-button review-tooltip-add-comment-button"
|
||||
onClick={onAddComment}
|
||||
>
|
||||
<MaterialIcon type="chat" />
|
||||
{t('add_comment')}
|
||||
</button>
|
||||
{showChangesButtons && (
|
||||
<>
|
||||
<div className="review-tooltip-menu-divider" />
|
||||
<OLTooltip
|
||||
id="accept-all-changes"
|
||||
description={t('accept_selected_changes')}
|
||||
>
|
||||
<MaterialIcon type="check" />
|
||||
</button>
|
||||
</OLTooltip>
|
||||
<button
|
||||
className="review-tooltip-menu-button"
|
||||
onClick={acceptChangesHandler}
|
||||
aria-label={t('accept_selected_changes')}
|
||||
>
|
||||
<MaterialIcon type="check" />
|
||||
</button>
|
||||
</OLTooltip>
|
||||
|
||||
<OLTooltip
|
||||
id="reject-all-changes"
|
||||
description={t('reject_selected_changes')}
|
||||
>
|
||||
<button
|
||||
className="review-tooltip-menu-button"
|
||||
onClick={rejectChangesHandler}
|
||||
aria-label={t('reject_selected_changes')}
|
||||
<OLTooltip
|
||||
id="reject-all-changes"
|
||||
description={t('reject_selected_changes')}
|
||||
>
|
||||
<MaterialIcon type="clear" />
|
||||
</button>
|
||||
</OLTooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<button
|
||||
className="review-tooltip-menu-button"
|
||||
onClick={rejectChangesHandler}
|
||||
aria-label={t('reject_selected_changes')}
|
||||
>
|
||||
<MaterialIcon type="clear" />
|
||||
</button>
|
||||
</OLTooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default ReviewTooltipMenu
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
import React, { memo } from 'react'
|
||||
import unfilledIconTypes from '../../../fonts/material-symbols/unfilled-symbols.mjs'
|
||||
|
||||
export type AvailableUnfilledIcon = (typeof unfilledIconTypes)[number]
|
||||
@@ -53,4 +53,4 @@ function MaterialIcon({
|
||||
)
|
||||
}
|
||||
|
||||
export default MaterialIcon
|
||||
export default memo(MaterialIcon)
|
||||
|
||||
Reference in New Issue
Block a user