mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #20583 from overleaf/dk-review-gotoposition
Goto position when clicking on review entry in Overview tab GitOrigin-RevId: 17c9cefed339537afa994778ffbc01cfb294bf46
This commit is contained in:
parent
e253b48bc0
commit
2801b3baad
6 changed files with 52 additions and 20 deletions
|
@ -15,11 +15,12 @@ import { ThreadId } from '../../../../../types/review-panel/review-panel'
|
||||||
import { Decoration } from '@codemirror/view'
|
import { Decoration } from '@codemirror/view'
|
||||||
|
|
||||||
export const ReviewPanelAddComment: FC<{
|
export const ReviewPanelAddComment: FC<{
|
||||||
|
docId: string
|
||||||
from: number
|
from: number
|
||||||
to: number
|
to: number
|
||||||
value: Decoration
|
value: Decoration
|
||||||
top: number | undefined
|
top: number | undefined
|
||||||
}> = ({ from, to, value, top }) => {
|
}> = ({ from, to, value, top, docId }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const view = useCodeMirrorViewContext()
|
const view = useCodeMirrorViewContext()
|
||||||
const state = useCodeMirrorStateContext()
|
const state = useCodeMirrorStateContext()
|
||||||
|
@ -113,6 +114,7 @@ export const ReviewPanelAddComment: FC<{
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReviewPanelEntry
|
<ReviewPanelEntry
|
||||||
|
docId={docId}
|
||||||
top={top}
|
top={top}
|
||||||
position={from}
|
position={from}
|
||||||
op={{
|
op={{
|
||||||
|
|
|
@ -21,7 +21,9 @@ export const ReviewPanelChange = memo<{
|
||||||
aggregate?: Change<DeleteOperation>
|
aggregate?: Change<DeleteOperation>
|
||||||
top?: number
|
top?: number
|
||||||
editable?: boolean
|
editable?: boolean
|
||||||
}>(({ change, aggregate, top, editable = true }) => {
|
docId: string
|
||||||
|
hoverRanges?: boolean
|
||||||
|
}>(({ change, aggregate, top, docId, hoverRanges, editable = true }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { acceptChanges, rejectChanges } = useRangesActionsContext()
|
const { acceptChanges, rejectChanges } = useRangesActionsContext()
|
||||||
const permissions = usePermissionsContext()
|
const permissions = usePermissionsContext()
|
||||||
|
@ -42,6 +44,8 @@ export const ReviewPanelChange = memo<{
|
||||||
top={top}
|
top={top}
|
||||||
op={change.op}
|
op={change.op}
|
||||||
position={change.op.p}
|
position={change.op.p}
|
||||||
|
docId={docId}
|
||||||
|
hoverRanges={hoverRanges}
|
||||||
>
|
>
|
||||||
<div className="review-panel-entry-indicator">
|
<div className="review-panel-entry-indicator">
|
||||||
<MaterialIcon type="edit" className="review-panel-entry-icon" />
|
<MaterialIcon type="edit" className="review-panel-entry-icon" />
|
||||||
|
|
|
@ -8,8 +8,10 @@ import { ReviewPanelCommentContent } from './review-panel-comment-content'
|
||||||
|
|
||||||
export const ReviewPanelComment = memo<{
|
export const ReviewPanelComment = memo<{
|
||||||
comment: Change<CommentOperation>
|
comment: Change<CommentOperation>
|
||||||
|
docId: string
|
||||||
top?: number
|
top?: number
|
||||||
}>(({ comment, top }) => {
|
hoverRanges?: boolean
|
||||||
|
}>(({ comment, top, docId, hoverRanges }) => {
|
||||||
const threads = useThreadsContext()
|
const threads = useThreadsContext()
|
||||||
|
|
||||||
const thread = threads?.[comment.op.t]
|
const thread = threads?.[comment.op.t]
|
||||||
|
@ -22,9 +24,11 @@ export const ReviewPanelComment = memo<{
|
||||||
className={classnames('review-panel-entry-comment', {
|
className={classnames('review-panel-entry-comment', {
|
||||||
'review-panel-entry-loaded': !!threads?.[comment.op.t],
|
'review-panel-entry-loaded': !!threads?.[comment.op.t],
|
||||||
})}
|
})}
|
||||||
|
docId={docId}
|
||||||
top={top}
|
top={top}
|
||||||
op={comment.op}
|
op={comment.op}
|
||||||
position={comment.op.p}
|
position={comment.op.p}
|
||||||
|
hoverRanges={hoverRanges}
|
||||||
>
|
>
|
||||||
<div className="review-panel-entry-indicator">
|
<div className="review-panel-entry-indicator">
|
||||||
<MaterialIcon type="comment" className="review-panel-entry-icon" />
|
<MaterialIcon type="comment" className="review-panel-entry-icon" />
|
||||||
|
|
|
@ -251,6 +251,7 @@ const ReviewPanelCurrentFile: FC = () => {
|
||||||
const { id, from, to, value, top } = entry
|
const { id, from, to, value, top } = entry
|
||||||
return (
|
return (
|
||||||
<ReviewPanelAddComment
|
<ReviewPanelAddComment
|
||||||
|
docId={ranges!.docId}
|
||||||
key={id}
|
key={id}
|
||||||
from={from}
|
from={from}
|
||||||
to={to}
|
to={to}
|
||||||
|
@ -264,6 +265,7 @@ const ReviewPanelCurrentFile: FC = () => {
|
||||||
change =>
|
change =>
|
||||||
positions.has(change.id) && (
|
positions.has(change.id) && (
|
||||||
<ReviewPanelChange
|
<ReviewPanelChange
|
||||||
|
docId={ranges!.docId}
|
||||||
key={change.id}
|
key={change.id}
|
||||||
change={change}
|
change={change}
|
||||||
top={positions.get(change.id)}
|
top={positions.get(change.id)}
|
||||||
|
@ -276,6 +278,7 @@ const ReviewPanelCurrentFile: FC = () => {
|
||||||
comment =>
|
comment =>
|
||||||
positions.has(comment.id) && (
|
positions.has(comment.id) && (
|
||||||
<ReviewPanelComment
|
<ReviewPanelComment
|
||||||
|
docId={ranges!.docId}
|
||||||
key={comment.id}
|
key={comment.id}
|
||||||
comment={comment}
|
comment={comment}
|
||||||
top={positions.get(comment.id)}
|
top={positions.get(comment.id)}
|
||||||
|
|
|
@ -5,44 +5,56 @@ import {
|
||||||
useCodeMirrorViewContext,
|
useCodeMirrorViewContext,
|
||||||
} from '@/features/source-editor/components/codemirror-editor'
|
} from '@/features/source-editor/components/codemirror-editor'
|
||||||
import { isSelectionWithinOp } from '../utils/is-selection-within-op'
|
import { isSelectionWithinOp } from '../utils/is-selection-within-op'
|
||||||
import { EditorSelection } from '@codemirror/state'
|
|
||||||
import { EditorView } from '@codemirror/view'
|
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { highlightRanges } from '@/features/source-editor/extensions/ranges'
|
import { highlightRanges } from '@/features/source-editor/extensions/ranges'
|
||||||
|
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
||||||
|
|
||||||
export const ReviewPanelEntry: FC<{
|
export const ReviewPanelEntry: FC<{
|
||||||
position: number
|
position: number
|
||||||
op: AnyOperation
|
op: AnyOperation
|
||||||
|
docId: string
|
||||||
top?: number
|
top?: number
|
||||||
className?: string
|
className?: string
|
||||||
selectLineOnFocus?: boolean
|
selectLineOnFocus?: boolean
|
||||||
}> = ({ children, position, top, op, className, selectLineOnFocus = true }) => {
|
hoverRanges?: boolean
|
||||||
|
}> = ({
|
||||||
|
children,
|
||||||
|
position,
|
||||||
|
top,
|
||||||
|
op,
|
||||||
|
className,
|
||||||
|
selectLineOnFocus = true,
|
||||||
|
docId,
|
||||||
|
hoverRanges = true,
|
||||||
|
}) => {
|
||||||
const state = useCodeMirrorStateContext()
|
const state = useCodeMirrorStateContext()
|
||||||
const view = useCodeMirrorViewContext()
|
const view = useCodeMirrorViewContext()
|
||||||
|
const { openDocId } = useEditorManagerContext()
|
||||||
const [focused, setFocused] = useState(false)
|
const [focused, setFocused] = useState(false)
|
||||||
|
|
||||||
const highlighted = isSelectionWithinOp(op, state.selection.main)
|
const highlighted = isSelectionWithinOp(op, state.selection.main)
|
||||||
|
|
||||||
const focusHandler = useCallback(() => {
|
const focusHandler = useCallback(() => {
|
||||||
setTimeout(() => {
|
|
||||||
// without setTimeout, error "EditorView.update are not allowed while an update is in progress" can occur
|
|
||||||
// this can be avoided by using onClick rather than onFocus but it will then not pick up <Tab> or <Shift+Tab> events for focusing entries
|
|
||||||
if (selectLineOnFocus) {
|
if (selectLineOnFocus) {
|
||||||
view.dispatch({
|
openDocId(docId, { gotoOffset: position, keepCurrentView: true })
|
||||||
selection: EditorSelection.cursor(position),
|
|
||||||
effects: EditorView.scrollIntoView(position, { y: 'center' }),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, 0)
|
|
||||||
setFocused(true)
|
setFocused(true)
|
||||||
}, [view, position, selectLineOnFocus])
|
}, [selectLineOnFocus, docId, openDocId, position])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onFocus={focusHandler}
|
onFocus={focusHandler}
|
||||||
onBlur={() => setFocused(false)}
|
onBlur={() => setFocused(false)}
|
||||||
onMouseEnter={() => view.dispatch(highlightRanges(op))}
|
onMouseEnter={() => {
|
||||||
onMouseLeave={() => view.dispatch(highlightRanges())}
|
if (hoverRanges) {
|
||||||
|
view.dispatch(highlightRanges(op))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
if (hoverRanges) {
|
||||||
|
view.dispatch(highlightRanges())
|
||||||
|
}
|
||||||
|
}}
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={position + 1}
|
tabIndex={position + 1}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
|
|
@ -82,11 +82,18 @@ export const ReviewPanelOverviewFile: FC<{
|
||||||
change={change}
|
change={change}
|
||||||
aggregate={aggregates.get(change.id)}
|
aggregate={aggregates.get(change.id)}
|
||||||
editable={false}
|
editable={false}
|
||||||
|
docId={doc.doc.id}
|
||||||
|
hoverRanges={false}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{unresolvedComments.map(comment => (
|
{unresolvedComments.map(comment => (
|
||||||
<ReviewPanelComment key={comment.id} comment={comment} />
|
<ReviewPanelComment
|
||||||
|
key={comment.id}
|
||||||
|
comment={comment}
|
||||||
|
docId={doc.doc.id}
|
||||||
|
hoverRanges={false}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Reference in a new issue