Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 优化选中行 #1921

Merged
merged 3 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/devui-vue/devui/code-review/src/code-review-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export interface CommentPosition {
left: number;
right: number;
}
export type ILineNumberTdMap = Record<number, HTMLElement[]>;
export interface IExpandLineNumberInfo {
nextL: string;
nextR: string;
prevL: string;
prevR: string;
}
export interface ICheckedLineDetails {
lefts: number[];
rights: number[];
codes: Record<string, string[]> | string[];
}
export interface CodeReviewMethods {
toggleFold: (status?: boolean) => void;
insertComment: (lineNumber: number, lineSide: LineSide, commentDom: HTMLElement) => void;
Expand Down
11 changes: 6 additions & 5 deletions packages/devui-vue/devui/code-review/src/code-review.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @jsxImportSource vue */
import { defineComponent, onMounted, provide, toRefs } from 'vue';
import { defineComponent, onMounted, provide, toRefs, ref } from 'vue';
import type { SetupContext } from 'vue';
import CodeReviewHeader from './components/code-review-header';
import { CommentIcon } from './components/code-review-icons';
Expand All @@ -18,8 +18,7 @@ export default defineComponent({
setup(props: CodeReviewProps, ctx: SetupContext) {
const ns = useNamespace('code-review');
const { diffType } = toRefs(props);
const { renderHtml, reviewContentRef, diffFile, onContentClick } = useCodeReview(props, ctx);
const { isFold, toggleFold } = useCodeReviewFold(props, ctx);
const reviewContentRef = ref();
const {
commentLeft,
commentTop,
Expand All @@ -28,16 +27,18 @@ export default defineComponent({
onCommentIconClick,
insertComment,
removeComment,
updateCheckedLineClass,
clearCheckedLines,
updateLineNumberMap,
updateCheckedLine,
} = useCodeReviewComment(reviewContentRef, props, ctx);
const { renderHtml, diffFile, onContentClick } = useCodeReview(props, ctx, reviewContentRef, updateLineNumberMap, updateCheckedLine);
const { isFold, toggleFold } = useCodeReviewFold(props, ctx);

onMounted(() => {
ctx.emit('afterViewInit', {
toggleFold,
insertComment,
removeComment,
updateCheckedLineClass,
clearCheckedLines,
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ref, toRefs, onUnmounted, watch } from 'vue';
import { ref, toRefs, onUnmounted } from 'vue';
import type { SetupContext, Ref } from 'vue';
import { useCodeReviewLineSelection } from './use-code-review-line-selection';
import type { LineSide, CodeReviewProps } from '../code-review-types';
import type { LineSide, CodeReviewProps, ICheckedLineDetails } from '../code-review-types';
import { useNamespace } from '../../../shared/hooks/use-namespace';
import {
notEmptyNode,
Expand All @@ -14,28 +14,18 @@ import {
export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props: CodeReviewProps, ctx: SetupContext) {
const { outputFormat, allowComment, allowChecked } = toRefs(props);
const ns = useNamespace('code-review');
const { onMousedown } = useCodeReviewLineSelection(reviewContentRef, props, afterMouseup);
const { onMousedown, updateLineNumberMap, getCheckedLineDetails, clearCommentClass, updateCheckedLine } = useCodeReviewLineSelection(
reviewContentRef,
props,
afterMouseup
);
const commentLeft = ref(-100);
const commentTop = ref(-100);
let currentLeftLineNumber = -1;
let currentRightLineNumber = -1;
let currentPosition: 'left' | 'right';
let lastLineNumberContainer: HTMLElement | null;
let checkedLineNumberContainer: Array<Element> = [];
let currentLeftLineNumbers: Array<number> = [];
let currentRightLineNumbers: Array<number> = [];
let checkedLineCodeString: Array<string> | Record<string, Array<string>> = {};
let allTrNodes: NodeListOf<Element> = [];
let afterCheckLinesEmitData: Record<string, any>;
watch(
() => outputFormat.value,
() => {
// 如果出现单栏双栏切换则需要重置选中
checkedLineNumberContainer = [];
currentLeftLineNumbers = [];
currentRightLineNumbers = [];
checkedLineCodeString = [];
}
);

const resetLeftTop = () => {
commentLeft.value = -100;
commentTop.value = -100;
Expand Down Expand Up @@ -85,6 +75,8 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
commentLeft.value = left;
commentTop.value = top;
currentLeftLineNumber = parseInt(leftLineNumberContainer.innerText);
currentRightLineNumber = parseInt(rightLineNumberContainer.innerText || '-1');
currentPosition = 'left';
} else {
resetLeftTop();
}
Expand All @@ -98,7 +90,9 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
const { top, left } = rightLineNumberContainer.getBoundingClientRect();
commentLeft.value = left;
commentTop.value = top;
currentLeftLineNumber = parseInt(leftLineNumberContainer.innerText || '-1');
currentRightLineNumber = parseInt(rightLineNumberContainer.innerText);
currentPosition = 'right';
} else {
resetLeftTop();
}
Expand All @@ -117,150 +111,27 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
resetLeftTop();
}
};
// 获取一些公共类和判断
const getCommonClassAndJudge = () => {
const checkedLine = [currentLeftLineNumbers, currentRightLineNumbers];
return {
linenumberDom: allTrNodes,
checkedLine,
};
};
// 之前每次都先移出所有选中的方法过于浪费性能,增加具体dom节点选中方法(防重复添加)
const addCommentCheckedClass = (Dom: Element) => {
!Dom.classList.contains('comment-checked') && Dom.classList.add('comment-checked');
};
// 单栏
function getSingleCheckedLineCode(shouldRenderClass: boolean) {
const { linenumberDom, checkedLine } = getCommonClassAndJudge();
const checkedCodeContent = [];
for (let i = 0; i < linenumberDom.length; i++) {
const lineNumberDomLeft = linenumberDom[i].children[0];
const lineNumberDomRight = linenumberDom[i].children[1];
if (lineNumberDomLeft || lineNumberDomRight) {
const codeLineNumberLeft = parseInt((lineNumberDomLeft as HTMLElement)?.innerText);
const codeLineNumberRight = parseInt((lineNumberDomRight as HTMLElement)?.innerText);
// 因为存在左边或者右边为空的num所以两边都要循环,但是同一个dom已经过就不需要再赋予
if (checkedLine[0].includes(codeLineNumberLeft) || checkedLine[1].includes(codeLineNumberRight)) {
checkedLineNumberContainer.push(linenumberDom[i]);
// 两个节点之间可能间隔文本节点
const codeNode = linenumberDom[i].nextElementSibling as HTMLElement;
checkedCodeContent.push(codeNode?.innerText);
if (shouldRenderClass) {
addCommentCheckedClass(linenumberDom[i]);
addCommentCheckedClass(codeNode);
}
}
}
}
checkedLineCodeString = checkedCodeContent;
}
// 双栏
function getDoubleCheckedLineCode(shouldRenderClass: boolean) {
const { linenumberDom, checkedLine } = getCommonClassAndJudge();
const checkedCodeContentLeft = [];
const checkedCodeContentRight = [];

function checkedFunc(Dom: Element) {
checkedLineNumberContainer.push(Dom);
const codeNode = Dom.nextElementSibling as HTMLElement;
if (shouldRenderClass) {
addCommentCheckedClass(Dom);
addCommentCheckedClass(codeNode);
}
return codeNode?.innerText;
}

for (let i = 0; i < linenumberDom.length; i++) {
// 左右双栏一起遍历
const codeLineNumber = parseInt(linenumberDom[i]?.innerHTML);
if (linenumberDom[i].classList.contains('d-code-left') && checkedLine[0].includes(codeLineNumber)) {
const lineNumText = checkedFunc(linenumberDom[i]);
checkedCodeContentLeft.push(lineNumText);
continue;
}
if (linenumberDom[i].classList.contains('d-code-right') && checkedLine[1].includes(codeLineNumber)) {
const lineNumText = checkedFunc(linenumberDom[i]);
checkedCodeContentRight.push(lineNumText);
}
}
checkedLineCodeString = { leftCode: checkedCodeContentLeft, rightCode: checkedCodeContentRight };
}
function getCheckedLineCode(shouldRenderClass: boolean) {
if (props.outputFormat === 'line-by-line') {
return getSingleCheckedLineCode(shouldRenderClass);
}
getDoubleCheckedLineCode(shouldRenderClass);
}
function updateLineNumbers({ lefts, rights }: { lefts: number[]; rights: number[] }) {
currentLeftLineNumbers = lefts;
currentRightLineNumbers = rights;
getCheckedLineCode(false);
afterCheckLinesEmitData = {
left: currentLeftLineNumber,
right: currentRightLineNumber,
details: {
lefts: currentLeftLineNumbers,
rights: currentRightLineNumbers,
codes: checkedLineCodeString,
},
};
}
const updateCheckedLineClass = () => {
const lineClassName = props.outputFormat === 'line-by-line' ? '.d2h-code-linenumber' : '.d2h-code-side-linenumber';
allTrNodes = reviewContentRef.value.querySelectorAll(lineClassName);
getCheckedLineCode(true);
};
// 还原样式
const resetCommentClass = () => {
for (let i = 0; i < checkedLineNumberContainer.length; i++) {
checkedLineNumberContainer[i].classList.remove('comment-checked');
const codeNode = checkedLineNumberContainer[i].nextElementSibling;
(codeNode as HTMLElement)?.classList.remove('comment-checked');
}
checkedLineNumberContainer = [];
};
// 点击
const commentClick = () => {
interface recordType {
left: number;
right: number;
details?: {
lefts: Array<number>;
rights: Array<number>;
codes: Record<string, Array<string>> | Record<string, Array<number>>;
};
}
let obj: recordType = { left: currentLeftLineNumber, right: currentRightLineNumber };
if ((currentLeftLineNumbers.length >= 1 || currentRightLineNumbers.length >= 1) && allowChecked.value) {
// 选中模式
const maxCurrentLeftLineNumber = currentLeftLineNumbers[currentLeftLineNumbers.length - 1];
const maxCurrentRightLineNumber = currentRightLineNumbers[currentRightLineNumbers.length - 1];
if (maxCurrentLeftLineNumber === currentLeftLineNumber || maxCurrentRightLineNumber === currentRightLineNumber) {
// 点击添加评论图标触发的事件
obj = {
left: currentLeftLineNumber,
right: currentRightLineNumber,
details: {
lefts: currentLeftLineNumbers,
rights: currentRightLineNumbers,
codes: checkedLineCodeString,
},
};
let obj = { left: currentLeftLineNumber, right: currentRightLineNumber, position: currentPosition };
const checkedLineDetails = getCheckedLineDetails();
// 多行选中
if (checkedLineDetails && allowChecked.value) {
const { lefts, rights } = checkedLineDetails;
const maxCheckedLeftLineNumber = lefts[lefts.length - 1];
const maxCheckedRightLineNumber = rights[rights.length - 1];
if (maxCheckedLeftLineNumber === currentLeftLineNumber || maxCheckedRightLineNumber === currentRightLineNumber) {
obj.details = checkedLineDetails;
} else {
currentLeftLineNumbers = [];
currentRightLineNumbers = [];
resetCommentClass();
clearCommentClass();
}
}
// 点击添加评论图标触发的事件
ctx.emit('addComment', obj);
};
function afterCheckLines() {
ctx.emit('afterCheckLines', afterCheckLinesEmitData);
}
function afterMouseup(lineNumbers: { lefts: number[]; rights: number[] }) {
updateLineNumbers(lineNumbers);
afterCheckLines();
function afterMouseup(details: ICheckedLineDetails) {
ctx.emit('afterCheckLines', { left: currentLeftLineNumber, right: currentRightLineNumber, position: currentPosition, details });
}
// 图标或者单行的点击
const onCommentIconClick = (e: Event) => {
Expand Down Expand Up @@ -317,16 +188,7 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
};

const clearCheckedLines = () => {
currentLeftLineNumbers = [];
currentRightLineNumbers = [];
checkedLineCodeString = [];
resetCommentClass();
};

const handleMouseDown = (e: MouseEvent) => {
const lineClassName = props.outputFormat === 'line-by-line' ? '.d2h-code-linenumber' : '.d2h-code-side-linenumber';
allTrNodes = reviewContentRef.value.querySelectorAll(lineClassName);
onMousedown(e);
clearCommentClass();
};

const mouseEvent: Record<string, (e: MouseEvent) => void> = {};
Expand All @@ -335,7 +197,7 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
mouseEvent.onMouseleave = onMouseleave;
}
if (props.allowChecked) {
mouseEvent.onMousedown = handleMouseDown;
mouseEvent.onMousedown = onMousedown;
}

window.addEventListener('scroll', resetLeftTop);
Expand All @@ -348,11 +210,12 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
commentLeft,
commentTop,
mouseEvent,
updateCheckedLineClass,
clearCheckedLines,
onCommentMouseLeave,
onCommentIconClick,
insertComment,
removeComment,
updateLineNumberMap,
updateCheckedLine,
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { toRefs } from 'vue';
import type { Ref } from 'vue';
import type { CodeReviewProps, ExpandDirection } from '../code-review-types';
import type { CodeReviewProps, ExpandDirection, IExpandLineNumberInfo } from '../code-review-types';
import { ExpandLineReg, FirstLineReg } from '../const';
import {
attachExpandUpDownButton,
Expand All @@ -14,7 +14,12 @@ import {
ifRemoveExpandLineForDoubleColumn,
} from '../utils';

export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, props: CodeReviewProps) {
export function useCodeReviewExpand(
reviewContentRef: Ref<HTMLElement>,
props: CodeReviewProps,
updateLineNumberMap: (expandLineNumberInfo: IExpandLineNumberInfo, newCode: string, direction: 'up' | 'down') => void,
updateCheckedLine: (expandLineNumberInfo: IExpandLineNumberInfo, direction: 'up' | 'down') => void
) {
const { outputFormat, expandThreshold, expandLoader } = toRefs(props);

const processSideBySide = () => {
Expand Down Expand Up @@ -85,8 +90,12 @@ export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, props: C

// 过滤有效行
const trNodesToBeInserted = trNodes.filter((element) => element !== expandLine);
/* 更新左右行号映射关系 */
updateLineNumberMap(referenceDom.dataset as unknown as IExpandLineNumberInfo, prefix + code, direction);
// 将有效代码行插入页面
insertIncrementLineToPage(referenceDom, trNodesToBeInserted, direction);
/* 若新增行在选中区间,则将新增行高亮 */
updateCheckedLine(referenceDom.dataset as unknown as IExpandLineNumberInfo, direction);

// 判断是否需要移除展开行,代码若已全部展开,不再需要展开行
const removedExpandLine = ifRemoveExpandLineForDoubleColumn(referenceDom, expandLine, direction);
Expand Down Expand Up @@ -192,6 +201,8 @@ export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, props: C
const trNodesToBeInserted = trNodes.filter((element) => element.children[0].children.length === 2);
// 将有效代码行插入页面
insertIncrementLineToPage(referenceDom, trNodesToBeInserted, direction);
/* 若新增行在选中区间,则将新增行高亮 */
updateCheckedLine(referenceDom.dataset as unknown as IExpandLineNumberInfo, direction);

// 判断是否需要移除展开行,代码若已全部展开,不再需要展开行
const removedExpandLine = ifRemoveExpandLine(referenceDom, expandLine, direction);
Expand Down
Loading
Loading