2024-10-04 05:15:31 -04:00
|
|
|
import { FC, useCallback, useEffect, useState } from 'react'
|
2024-08-15 06:07:40 -04:00
|
|
|
import { AnyOperation } from '../../../../../types/change'
|
|
|
|
import {
|
|
|
|
useCodeMirrorStateContext,
|
|
|
|
useCodeMirrorViewContext,
|
2024-09-27 06:31:31 -04:00
|
|
|
} from '@/features/source-editor/components/codemirror-context'
|
2024-08-15 06:07:40 -04:00
|
|
|
import { isSelectionWithinOp } from '../utils/is-selection-within-op'
|
|
|
|
import classNames from 'classnames'
|
2024-10-02 08:41:47 -04:00
|
|
|
import {
|
|
|
|
clearHighlightRanges,
|
|
|
|
highlightRanges,
|
|
|
|
} from '@/features/source-editor/extensions/ranges'
|
2024-09-26 10:13:54 -04:00
|
|
|
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
2024-10-04 05:15:46 -04:00
|
|
|
import { useLayoutContext } from '@/shared/context/layout-context'
|
2024-08-15 06:07:40 -04:00
|
|
|
|
|
|
|
export const ReviewPanelEntry: FC<{
|
|
|
|
position: number
|
|
|
|
op: AnyOperation
|
2024-09-26 10:13:54 -04:00
|
|
|
docId: string
|
2024-08-15 06:07:40 -04:00
|
|
|
top?: number
|
|
|
|
className?: string
|
2024-09-16 04:40:59 -04:00
|
|
|
selectLineOnFocus?: boolean
|
2024-09-26 10:13:54 -04:00
|
|
|
hoverRanges?: boolean
|
|
|
|
}> = ({
|
|
|
|
children,
|
|
|
|
position,
|
|
|
|
top,
|
|
|
|
op,
|
|
|
|
className,
|
|
|
|
selectLineOnFocus = true,
|
|
|
|
docId,
|
|
|
|
hoverRanges = true,
|
|
|
|
}) => {
|
2024-08-15 06:07:40 -04:00
|
|
|
const state = useCodeMirrorStateContext()
|
|
|
|
const view = useCodeMirrorViewContext()
|
2024-09-26 10:13:54 -04:00
|
|
|
const { openDocId } = useEditorManagerContext()
|
2024-08-15 06:07:40 -04:00
|
|
|
const [focused, setFocused] = useState(false)
|
2024-10-04 05:15:46 -04:00
|
|
|
const { setReviewPanelOpen } = useLayoutContext()
|
2024-08-15 06:07:40 -04:00
|
|
|
|
|
|
|
const highlighted = isSelectionWithinOp(op, state.selection.main)
|
|
|
|
|
2024-10-04 05:15:46 -04:00
|
|
|
const openReviewPanel = useCallback(() => {
|
|
|
|
setReviewPanelOpen(true)
|
|
|
|
}, [setReviewPanelOpen])
|
|
|
|
|
2024-10-08 08:23:30 -04:00
|
|
|
const focusHandler = useCallback(
|
|
|
|
event => {
|
|
|
|
if (
|
|
|
|
event.target instanceof HTMLButtonElement ||
|
|
|
|
event.target instanceof HTMLLinkElement ||
|
|
|
|
event.target instanceof HTMLTextAreaElement
|
|
|
|
) {
|
|
|
|
// Don't focus if the click was on a button/link/textarea as we don't want
|
|
|
|
// affect the behaviour of the button/link/textarea
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectLineOnFocus) {
|
|
|
|
openDocId(docId, { gotoOffset: position, keepCurrentView: true })
|
|
|
|
}
|
|
|
|
setFocused(true)
|
|
|
|
},
|
|
|
|
[selectLineOnFocus, docId, openDocId, position]
|
|
|
|
)
|
2024-08-15 06:07:40 -04:00
|
|
|
|
2024-10-04 05:15:31 -04:00
|
|
|
// Clear op highlight on dismount
|
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
if (hoverRanges) {
|
|
|
|
view.dispatch(clearHighlightRanges(op))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
|
2024-08-15 06:07:40 -04:00
|
|
|
return (
|
|
|
|
<div
|
2024-10-04 05:15:46 -04:00
|
|
|
onMouseDown={openReviewPanel} // Using onMouseDown rather than onClick to guarantee that it fires before onFocus
|
2024-08-15 06:07:40 -04:00
|
|
|
onFocus={focusHandler}
|
|
|
|
onBlur={() => setFocused(false)}
|
2024-09-26 10:13:54 -04:00
|
|
|
onMouseEnter={() => {
|
|
|
|
if (hoverRanges) {
|
|
|
|
view.dispatch(highlightRanges(op))
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
onMouseLeave={() => {
|
|
|
|
if (hoverRanges) {
|
2024-10-02 08:41:47 -04:00
|
|
|
view.dispatch(clearHighlightRanges(op))
|
2024-09-26 10:13:54 -04:00
|
|
|
}
|
|
|
|
}}
|
2024-08-15 06:07:40 -04:00
|
|
|
role="button"
|
|
|
|
tabIndex={position + 1}
|
|
|
|
className={classNames(
|
|
|
|
'review-panel-entry',
|
|
|
|
{
|
|
|
|
'review-panel-entry-focused': focused,
|
|
|
|
'review-panel-entry-highlighted': highlighted,
|
|
|
|
},
|
|
|
|
className
|
|
|
|
)}
|
|
|
|
data-top={top}
|
|
|
|
data-pos={position}
|
|
|
|
style={{
|
|
|
|
position: top === undefined ? 'relative' : 'absolute',
|
|
|
|
visibility: top === undefined ? 'visible' : 'hidden',
|
|
|
|
transition: 'top .3s, left .1s, right .1s',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|