Skip to content

Commit fda5e91

Browse files
committed
Merge branch 'dev'
2 parents e301e26 + 6532139 commit fda5e91

File tree

30 files changed

+860
-541
lines changed

30 files changed

+860
-541
lines changed

packages/devui-vue/devui/code-review/src/code-review-types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { DiffFile } from 'diff2html/lib/types';
44
export type OutputFormat = 'line-by-line' | 'side-by-side';
55
export type ExpandDirection = 'up' | 'down' | 'updown' | 'all';
66
export type LineSide = 'left' | 'right';
7+
export type IncrementCodeInsertDirection = 'up' | 'down';
78
export interface CommentPosition {
89
left: number;
910
right: number;
@@ -33,6 +34,9 @@ export const codeReviewProps = {
3334
type: Number,
3435
default: 50,
3536
},
37+
codeLoader: {
38+
type: Function as PropType<(interval: Array<number | undefined>, update: (code: string) => void) => void>,
39+
},
3640
};
3741
export type CodeReviewProps = ExtractPropTypes<typeof codeReviewProps>;
3842

packages/devui-vue/devui/code-review/src/code-review.scss

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@import 'highlight.js/styles/github.css';
12
@import 'diff2html/bundles/css/diff2html.min.css';
23
@import '../../styles-var/devui-var.scss';
34

@@ -77,6 +78,12 @@
7778

7879
tr {
7980
border: none;
81+
82+
&:hover {
83+
.d2h-info {
84+
background-color: #bfcbf3;
85+
}
86+
}
8087
}
8188

8289
th,
@@ -86,15 +93,43 @@
8693
}
8794

8895
td {
96+
&:first-child {
97+
position: static;
98+
display: table-cell;
99+
}
100+
89101
&.expand-icon-wrapper {
90102
text-align: center;
91103
}
92104

93105
.expand-icon {
94-
display: inline-block;
106+
display: flex;
107+
align-items: center;
108+
justify-content: center;
109+
width: 100%;
110+
height: 24px;
111+
cursor: pointer;
112+
113+
&:hover {
114+
background-color: $devui-primary;
115+
116+
svg g g g g {
117+
fill: $devui-light-text;
118+
}
119+
120+
svg g g polygon,
121+
svg g g path {
122+
fill: $devui-light-text;
123+
}
124+
}
95125
}
96126
}
97127

128+
.expand-line {
129+
height: 24px;
130+
line-height: 24px;
131+
}
132+
98133
.d2h-file-header {
99134
display: none;
100135
}
@@ -103,6 +138,10 @@
103138
border: none;
104139
}
105140

141+
.d2h-code-linenumber:after {
142+
content: '';
143+
}
144+
106145
.d2h-code-line-ctn {
107146
word-break: break-all;
108147
word-wrap: break-word !important;
@@ -113,7 +152,7 @@
113152

114153
.d2h-code-line {
115154
width: 100%;
116-
padding-left: 8em;
155+
padding-left: 0;
117156
padding-right: 16px;
118157
}
119158

packages/devui-vue/devui/code-review/src/code-review.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
import { defineComponent, onMounted } from 'vue';
1+
import { defineComponent, onMounted, provide } from 'vue';
22
import type { SetupContext } from 'vue';
33
import CodeReviewHeader from './components/code-review-header';
44
import { CommentIcon } from './components/code-review-icons';
5-
import { codeReviewProps } from './code-review-types';
5+
import { codeReviewProps, CodeReviewInjectionKey } from './code-review-types';
66
import type { CodeReviewProps } from './code-review-types';
77
import { useNamespace } from '../../shared/hooks/use-namespace';
88
import { useCodeReview } from './composables/use-code-review';
9+
import { useCodeReviewFold } from './composables/use-code-review-fold';
910
import { useCodeReviewComment } from './composables/use-code-review-comment';
1011
import './code-review.scss';
1112

1213
export default defineComponent({
1314
name: 'DCodeReview',
1415
props: codeReviewProps,
15-
emits: ['foldChange', 'addComment', 'afterViewInit'],
16+
emits: ['foldChange', 'addComment', 'afterViewInit', 'contentRefresh'],
1617
setup(props: CodeReviewProps, ctx: SetupContext) {
1718
const ns = useNamespace('code-review');
18-
const { renderHtml, isFold, reviewContentRef, toggleFold } = useCodeReview(props, ctx);
19+
const { renderHtml, reviewContentRef, diffFile, onContentClick } = useCodeReview(props, ctx);
20+
const { isFold, toggleFold } = useCodeReviewFold(props, ctx);
1921
const {
2022
commentLeft,
2123
commentTop,
@@ -32,13 +34,16 @@ export default defineComponent({
3234
ctx.emit('afterViewInit', { toggleFold, insertComment, removeComment });
3335
});
3436

37+
provide(CodeReviewInjectionKey, { reviewContentRef, diffInfo: diffFile.value[0], isFold, rootCtx: ctx });
38+
3539
return () => (
3640
<div class={ns.b()}>
3741
<CodeReviewHeader onClick={() => (isFold.value = !isFold.value)} />
3842
<div
3943
class={[ns.e('content'), { 'hide-content': isFold.value }]}
4044
v-html={renderHtml.value}
4145
ref={reviewContentRef}
46+
onClick={onContentClick}
4247
onMouseenter={onMouseEnter}
4348
onMousemove={onMouseMove}
4449
onMouseleave={onMouseleave}></div>

packages/devui-vue/devui/code-review/src/composables/use-code-review-comment.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ref } from 'vue';
1+
import { ref, onUnmounted } from 'vue';
22
import type { SetupContext, Ref } from 'vue';
33
import type { LineSide } from '../code-review-types';
44
import { useNamespace } from '../../../shared/hooks/use-namespace';
@@ -91,6 +91,12 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, ctx: Se
9191
}
9292
};
9393

94+
window.addEventListener('scroll', resetLeftTop);
95+
96+
onUnmounted(() => {
97+
window.removeEventListener('scroll', resetLeftTop);
98+
});
99+
94100
return {
95101
commentLeft,
96102
commentTop,

packages/devui-vue/devui/code-review/src/composables/use-code-review-expand.ts

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1+
import { toRefs } from 'vue';
12
import type { Ref } from 'vue';
2-
import type { OutputFormat } from '../code-review-types';
3+
import type { CodeReviewProps, ExpandDirection } from '../code-review-types';
34
import { ExpandLineReg, FirstLineReg } from '../const';
4-
import { attachExpandUpDownButton } from '../utils';
5+
import {
6+
attachExpandUpDownButton,
7+
getLineNumberFormDataset,
8+
parseDiffCode,
9+
updateExpandLineCount,
10+
updateLineNumberInDataset,
11+
insertIncrementLineToPage,
12+
ifRemoveExpandLine,
13+
} from '../utils';
14+
15+
export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, props: CodeReviewProps) {
16+
const { outputFormat, expandAllThreshold, codeLoader } = toRefs(props);
517

6-
export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, expandAllThreshold: number, outputFormat: OutputFormat) {
718
const processSideBySide = () => {
819
const [leftTable, rightTable] = reviewContentRef.value.querySelectorAll('table');
920
const trNodes = Array.from(leftTable.querySelectorAll('tr'));
@@ -24,7 +35,8 @@ export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, expandAl
2435
} else if (lineIndex > 0 && lineIndex < totalLines - 1 && nextLineIndex in trNodes && prevLineIndex in trNodes) {
2536
const preLineChildren = Array.from(trNodes[prevLineIndex].children) as HTMLElement[];
2637
const nextLineChildren = Array.from(trNodes[nextLineIndex].children) as HTMLElement[];
27-
const isExpandAll = parseInt(nextLineChildren[0].innerText) - parseInt(preLineChildren[0].innerText) - 1 < expandAllThreshold;
38+
const isExpandAll =
39+
parseInt(nextLineChildren[0].innerText) - parseInt(preLineChildren[0].innerText) - 1 < expandAllThreshold.value;
2840
attachExpandUpDownButton(lineNumberBox, isExpandAll ? 'all' : 'updown');
2941
}
3042
}
@@ -59,34 +71,99 @@ export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, expandAl
5971
const nextLineIndex = lineIndex + 1;
6072
const prevLineIndex = lineIndex - 1;
6173
if (lineIndex === 0 && nextLineIndex in trNodes) {
74+
updateLineNumberInDataset(trNodes[lineIndex], expandAllThreshold.value, 'top');
6275
attachExpandUpDownButton(lineNumberBox, 'up');
6376
} else if (lineIndex > 0 && lineIndex < totalLines - 1 && nextLineIndex in trNodes && prevLineIndex in trNodes) {
6477
const prevTdNodes = Array.from(trNodes[prevLineIndex].children) as HTMLElement[];
6578
const prevLineNumber = parseInt((prevTdNodes[0].children[0] as HTMLElement).innerText);
6679
const nextTdNodes = Array.from(trNodes[nextLineIndex].children) as HTMLElement[];
6780
const nextLineNumber = parseInt((nextTdNodes[0].children[0] as HTMLElement).innerText);
68-
const isExpandAll = nextLineNumber - prevLineNumber - 1 < expandAllThreshold;
81+
updateLineNumberInDataset(trNodes[lineIndex], expandAllThreshold.value, 'middle');
82+
const isExpandAll = nextLineNumber - prevLineNumber <= expandAllThreshold.value;
6983
attachExpandUpDownButton(lineNumberBox, isExpandAll ? 'all' : 'updown');
7084
}
7185
}
7286
}
7387

74-
const loadMoreLine = trNodes[0].cloneNode(true) as HTMLElement;
88+
const loadMoreLine = trNodes[0].cloneNode(true) as HTMLTableRowElement;
7589
loadMoreLine.children[0].removeChild(loadMoreLine.children[0].children[0]);
7690
(loadMoreLine.children[1] as HTMLElement).innerText = '';
77-
(loadMoreLine.children[1] as HTMLElement).style.height = '20px';
78-
const lastTrNode = trNodes[totalLines - 1].children;
79-
const leftLineStart = parseInt((lastTrNode[0].children[0] as HTMLElement).innerText) + 1;
80-
const rightLineStart = parseInt((lastTrNode[0].children[1] as HTMLElement).innerText) + 1;
81-
if (leftLineStart && rightLineStart) {
82-
attachExpandUpDownButton(loadMoreLine.children[0] as HTMLElement, 'down');
83-
}
8491
trNodes[0].parentElement?.appendChild(loadMoreLine);
92+
updateLineNumberInDataset(loadMoreLine, expandAllThreshold.value, 'bottom');
93+
attachExpandUpDownButton(loadMoreLine.children[0] as HTMLElement, 'down');
94+
};
95+
96+
const insertIncrementCode = (code: string, direction: 'up' | 'down', referenceDom: HTMLElement | null | undefined) => {
97+
if (!referenceDom) {
98+
return;
99+
}
100+
// 无更新内容,删除展开按钮所在行
101+
if (!code) {
102+
return referenceDom?.remove();
103+
}
104+
const prefix = '--- updated_at\tJan 1, 2019, 0:0:0 AM\n+++ updated_at\tJan 1, 2019, 0:0:0 AM\n';
105+
const container = document.createElement('div');
106+
// 解析code
107+
parseDiffCode(container, prefix + code, outputFormat.value);
108+
109+
const trNodes = Array.from(container.querySelectorAll('tr'));
110+
const expandLine = trNodes.find((element) => (element.children[1] as HTMLElement)?.innerText.trim().match(ExpandLineReg));
111+
// 不包含 @@ -x,x +y,y @@,视为无效code
112+
if (!expandLine) {
113+
return;
114+
}
115+
116+
// 将有效代码行插入页面
117+
insertIncrementLineToPage(referenceDom, trNodes, direction);
118+
119+
// 判断是否需要移除展开行,代码若已全部展开,不再需要展开行
120+
const removedExpandLine = ifRemoveExpandLine(referenceDom, expandLine, direction);
121+
// 已经移除展开行,则不需要更新展开行的内容和边界数据
122+
if (removedExpandLine) {
123+
return;
124+
}
125+
126+
// 更新展开行内容,@@ -x,x +y,y @@
127+
updateExpandLineCount(referenceDom, expandLine);
128+
129+
// 更新展开行节点保存的边界数据
130+
if (direction === 'up') {
131+
if (!referenceDom.previousElementSibling) {
132+
// 向上展开在第一行
133+
updateLineNumberInDataset(referenceDom, expandAllThreshold.value, 'top');
134+
} else {
135+
// 向上展开在中间行
136+
updateLineNumberInDataset(referenceDom, expandAllThreshold.value, 'middle', true);
137+
}
138+
} else {
139+
if (referenceDom.nextElementSibling) {
140+
// 向下展开在中间行
141+
updateLineNumberInDataset(referenceDom, expandAllThreshold.value, 'middle', true);
142+
} else {
143+
// 向下展开在末尾
144+
updateLineNumberInDataset(referenceDom, expandAllThreshold.value, 'bottom');
145+
}
146+
}
147+
};
148+
149+
const onExpandButtonClick = (e: Event) => {
150+
const composedPath = e.composedPath() as HTMLElement[];
151+
const expandIconDom = composedPath.find((element) => element.classList?.contains('expand-icon'));
152+
if (expandIconDom) {
153+
const expandDirection = Array.from(expandIconDom.classList)
154+
.find((item) => item.endsWith('expand'))
155+
?.split('-')[0];
156+
const direction: ExpandDirection = expandDirection === 'up' || expandDirection === 'all' ? 'up' : 'down';
157+
const [leftLineStart, leftLineEnd, rightLineStart, rightLineEnd] = getLineNumberFormDataset(expandIconDom, expandAllThreshold.value);
158+
codeLoader?.value?.([leftLineStart, leftLineEnd, rightLineStart, rightLineEnd], (code: string) => {
159+
insertIncrementCode(code, direction, expandIconDom.parentElement?.parentElement);
160+
});
161+
}
85162
};
86163

87164
const insertExpandButton = () => {
88-
outputFormat === 'side-by-side' ? processSideBySide() : processLineByLine();
165+
outputFormat.value === 'side-by-side' ? processSideBySide() : processLineByLine();
89166
};
90167

91-
return { insertExpandButton };
168+
return { insertExpandButton, onExpandButtonClick };
92169
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { toRefs, ref, watch } from 'vue';
2+
import type { SetupContext } from 'vue';
3+
import type { CodeReviewProps } from '../code-review-types';
4+
5+
export function useCodeReviewFold(props: CodeReviewProps, ctx: SetupContext) {
6+
const { fold } = toRefs(props);
7+
const isFold = ref(fold.value);
8+
9+
const toggleFold = (status?: boolean) => {
10+
if (status !== undefined) {
11+
isFold.value = status;
12+
} else {
13+
isFold.value = !isFold.value;
14+
}
15+
};
16+
17+
watch(fold, (val) => {
18+
isFold.value = val;
19+
});
20+
21+
watch(isFold, () => {
22+
ctx.emit('foldChange', isFold.value);
23+
});
24+
25+
return { isFold, toggleFold };
26+
}

0 commit comments

Comments
 (0)