1- import { ref , onUnmounted } from 'vue' ;
1+ import { ref , toRefs , onUnmounted } from 'vue' ;
22import type { SetupContext , Ref } from 'vue' ;
3- import type { LineSide } from '../code-review-types' ;
3+ import type { LineSide , CodeReviewProps } from '../code-review-types' ;
44import { useNamespace } from '../../../shared/hooks/use-namespace' ;
5- import { notEmptyNode , addCommentToPage } from '../utils' ;
5+ import {
6+ notEmptyNode ,
7+ addCommentToPageForSingleColumn ,
8+ addCommentToPageForDoubleColumn ,
9+ findReferenceDomForSingleColumn ,
10+ findReferenceDomForDoubleColumn ,
11+ } from '../utils' ;
612
7- export function useCodeReviewComment ( reviewContentRef : Ref < HTMLElement > , ctx : SetupContext ) {
13+ export function useCodeReviewComment ( reviewContentRef : Ref < HTMLElement > , props : CodeReviewProps , ctx : SetupContext ) {
14+ const { outputFormat, allowComment } = toRefs ( props ) ;
815 const ns = useNamespace ( 'code-review' ) ;
916 const commentLeft = ref ( - 100 ) ;
1017 const commentTop = ref ( - 100 ) ;
1118 let currentLeftLineNumber = - 1 ;
1219 let currentRightLineNumber = - 1 ;
13- let currentHoverTr : HTMLElement ;
14- let containerRect : DOMRect ;
20+ let lastLineNumberContainer : HTMLElement | null ;
1521
1622 const resetLeftTop = ( ) => {
1723 commentLeft . value = - 100 ;
1824 commentTop . value = - 100 ;
1925 currentLeftLineNumber = - 1 ;
2026 currentRightLineNumber = - 1 ;
21- } ;
22-
23- const onMouseEnter = ( e : MouseEvent ) => {
24- containerRect = ( e . currentTarget as HTMLElement ) . getBoundingClientRect ( ) ;
27+ lastLineNumberContainer ?. classList . remove ( 'comment-icon-hover' ) ;
28+ lastLineNumberContainer = null ;
2529 } ;
2630
2731 const onMouseMove = ( e : MouseEvent ) => {
2832 const composedPath = e . composedPath ( ) as HTMLElement [ ] ;
2933 const trNode = composedPath . find ( ( item ) => item . tagName === 'TR' ) ;
3034 if ( trNode ) {
31- const lineNumberContainer = Array . from ( trNode . children ) [ 0 ] as HTMLElement ;
32- if ( notEmptyNode ( lineNumberContainer ) ) {
33- const { top, left } = lineNumberContainer . getBoundingClientRect ( ) ;
34- commentLeft . value = left ;
35- commentTop . value = top ;
36- currentLeftLineNumber = parseInt ( ( lineNumberContainer . children [ 0 ] as HTMLElement ) ?. innerText ) || - 1 ;
37- currentRightLineNumber = parseInt ( ( lineNumberContainer . children [ 1 ] as HTMLElement ) ?. innerText ) || - 1 ;
38- currentHoverTr = trNode ;
35+ if ( outputFormat . value === 'line-by-line' ) {
36+ const lineNumberContainer = Array . from ( trNode . children ) [ 0 ] as HTMLElement ;
37+ if ( lastLineNumberContainer !== lineNumberContainer ) {
38+ lastLineNumberContainer ?. classList . remove ( 'comment-icon-hover' ) ;
39+ }
40+ if ( notEmptyNode ( lineNumberContainer ) ) {
41+ lastLineNumberContainer = lineNumberContainer ;
42+ lineNumberContainer . classList . add ( 'comment-icon-hover' ) ;
43+ const { top, left } = lineNumberContainer . getBoundingClientRect ( ) ;
44+ commentLeft . value = left ;
45+ commentTop . value = top ;
46+ currentLeftLineNumber = parseInt ( ( lineNumberContainer . children [ 0 ] as HTMLElement ) ?. innerText ) || - 1 ;
47+ currentRightLineNumber = parseInt ( ( lineNumberContainer . children [ 1 ] as HTMLElement ) ?. innerText ) || - 1 ;
48+ } else {
49+ resetLeftTop ( ) ;
50+ }
3951 } else {
40- resetLeftTop ( ) ;
52+ if ( trNode . classList . contains ( 'comment-block' ) ) {
53+ return resetLeftTop ( ) ;
54+ }
55+ const tdNode = composedPath . find ( ( item ) => item . tagName === 'TD' ) ;
56+ const tdIndex = Array . from ( trNode . children ) . findIndex ( ( item ) => item === tdNode ) ;
57+ const tdNodes = Array . from ( trNode . children ) as HTMLElement [ ] ;
58+ const leftLineNumberContainer = tdNodes [ 0 ] ;
59+ const rightLineNumberContainer = tdNodes [ 2 ] ;
60+ if ( tdIndex < 2 ) {
61+ if ( lastLineNumberContainer !== leftLineNumberContainer ) {
62+ lastLineNumberContainer ?. classList . remove ( 'comment-icon-hover' ) ;
63+ }
64+ if ( notEmptyNode ( leftLineNumberContainer ) ) {
65+ lastLineNumberContainer = leftLineNumberContainer ;
66+ leftLineNumberContainer . classList . add ( 'comment-icon-hover' ) ;
67+ const { top, left } = leftLineNumberContainer . getBoundingClientRect ( ) ;
68+ commentLeft . value = left ;
69+ commentTop . value = top ;
70+ currentLeftLineNumber = parseInt ( leftLineNumberContainer . innerText ) ;
71+ } else {
72+ resetLeftTop ( ) ;
73+ }
74+ } else {
75+ if ( lastLineNumberContainer !== rightLineNumberContainer ) {
76+ lastLineNumberContainer ?. classList . remove ( 'comment-icon-hover' ) ;
77+ }
78+ if ( rightLineNumberContainer && notEmptyNode ( rightLineNumberContainer ) ) {
79+ lastLineNumberContainer = rightLineNumberContainer ;
80+ rightLineNumberContainer . classList . add ( 'comment-icon-hover' ) ;
81+ const { top, left } = rightLineNumberContainer . getBoundingClientRect ( ) ;
82+ commentLeft . value = left ;
83+ commentTop . value = top ;
84+ currentRightLineNumber = parseInt ( rightLineNumberContainer . innerText ) ;
85+ } else {
86+ resetLeftTop ( ) ;
87+ }
88+ }
4189 }
4290 }
4391 } ;
@@ -53,44 +101,70 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, ctx: Se
53101 }
54102 } ;
55103
56- const onCommentIconClick = ( ) => {
57- ctx . emit ( 'addComment' , { left : currentLeftLineNumber , right : currentRightLineNumber } ) ;
58- } ;
59-
60- const findReferenceDom = ( lineNumber : number , lineSide : LineSide ) => {
61- const trNodes = Array . from ( reviewContentRef . value . querySelectorAll ( 'tr' ) ) ;
62- for ( const index in trNodes ) {
63- const lineIndex = parseInt ( index ) ;
64- const lineNumberBox = Array . from ( trNodes [ lineIndex ] . children ) [ 0 ] as HTMLElement ;
65- if ( notEmptyNode ( lineNumberBox ) ) {
66- const oldLineNumber = parseInt ( ( lineNumberBox . children [ 0 ] as HTMLElement ) ?. innerText ?? - 1 ) ;
67- const newLineNumber = parseInt ( ( lineNumberBox . children [ 1 ] as HTMLElement ) ?. innerText ?? - 1 ) ;
68-
69- if ( ( lineSide === 'left' && oldLineNumber === lineNumber ) || ( lineSide === 'right' && newLineNumber === lineNumber ) ) {
70- return trNodes [ lineIndex ] ;
71- }
104+ const onCommentIconClick = ( e : Event ) => {
105+ if ( e ) {
106+ const composedPath = e . composedPath ( ) as HTMLElement [ ] ;
107+ const lineNumberBox = composedPath . find (
108+ ( item ) => item . classList ?. contains ( 'comment-icon-hover' ) || item . classList ?. contains ( 'comment-icon' )
109+ ) ;
110+ if ( ! lineNumberBox ) {
111+ return ;
72112 }
73113 }
114+ const emitObj : Partial < Record < 'left' | 'right' , number > > = { } ;
115+ if ( outputFormat . value === 'line-by-line' ) {
116+ emitObj . left = currentLeftLineNumber ;
117+ emitObj . right = currentRightLineNumber ;
118+ } else if ( currentLeftLineNumber !== - 1 ) {
119+ emitObj . left = currentLeftLineNumber ;
120+ } else {
121+ emitObj . right = currentRightLineNumber ;
122+ }
123+ ctx . emit ( 'addComment' , { left : currentLeftLineNumber , right : currentRightLineNumber } ) ;
74124 } ;
75125
76126 const insertComment = ( lineNumber : number , lineSide : LineSide , commentDom : HTMLElement ) => {
77- const lineHost = findReferenceDom ( lineNumber , lineSide ) ;
78- lineHost && addCommentToPage ( lineHost , commentDom , lineSide ) ;
127+ if ( outputFormat . value === 'line-by-line' ) {
128+ const lineHost = findReferenceDomForSingleColumn ( reviewContentRef . value , lineNumber , lineSide ) ;
129+ lineHost && addCommentToPageForSingleColumn ( lineHost , commentDom , lineSide ) ;
130+ } else {
131+ const lineHost = findReferenceDomForDoubleColumn ( reviewContentRef . value , lineNumber , lineSide ) ;
132+ lineHost && addCommentToPageForDoubleColumn ( lineHost , commentDom , lineSide ) ;
133+ }
79134 } ;
80135
81136 const removeComment = ( lineNumber : number , lineSide : LineSide ) => {
82- const lineHost = findReferenceDom ( lineNumber , lineSide ) ;
83- let nextLineHost = lineHost ?. nextElementSibling ;
84- while ( nextLineHost ) {
85- const classList = nextLineHost ?. classList ;
86- if ( classList ?. contains ( 'comment-block' ) && classList . contains ( lineSide ) ) {
87- nextLineHost . remove ( ) ;
88- return ;
137+ if ( outputFormat . value === 'line-by-line' ) {
138+ const lineHost = findReferenceDomForSingleColumn ( reviewContentRef . value , lineNumber , lineSide ) ;
139+ let nextLineHost = lineHost ?. nextElementSibling ;
140+ while ( nextLineHost ) {
141+ const classList = nextLineHost ?. classList ;
142+ if ( classList ?. contains ( 'comment-block' ) && classList . contains ( lineSide ) ) {
143+ nextLineHost . remove ( ) ;
144+ return ;
145+ }
146+ nextLineHost = nextLineHost . nextElementSibling ;
147+ }
148+ } else {
149+ const lineHost = findReferenceDomForDoubleColumn ( reviewContentRef . value , lineNumber , lineSide ) ;
150+ const nextLineHost = lineHost ?. nextElementSibling ;
151+ if ( nextLineHost && nextLineHost . classList . contains ( 'comment-block' ) ) {
152+ const leftChildren = nextLineHost . children [ 0 ] ;
153+ const rightChildren = nextLineHost . children [ 1 ] ;
154+ if ( lineSide === 'left' ) {
155+ leftChildren . children [ 0 ] . remove ( ) ;
156+ } else {
157+ rightChildren . children [ 0 ] . remove ( ) ;
158+ }
159+ if ( ! leftChildren . children . length && ! rightChildren . children . length ) {
160+ nextLineHost . remove ( ) ;
161+ }
89162 }
90- nextLineHost = nextLineHost . nextElementSibling ;
91163 }
92164 } ;
93165
166+ const mouseEvent = allowComment . value ? { onMousemove : onMouseMove , onMouseleave : onMouseleave } : { } ;
167+
94168 window . addEventListener ( 'scroll' , resetLeftTop ) ;
95169
96170 onUnmounted ( ( ) => {
@@ -100,9 +174,7 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, ctx: Se
100174 return {
101175 commentLeft,
102176 commentTop,
103- onMouseEnter,
104- onMouseMove,
105- onMouseleave,
177+ mouseEvent,
106178 onCommentMouseLeave,
107179 onCommentIconClick,
108180 insertComment,
0 commit comments