Skip to content

Commit 4409d7d

Browse files
authored
Merge pull request #69 from rars/add-side-by-side-diff
feat(ngx-diff): add side-by-side diff component
2 parents 6bc1062 + f7b7c1d commit 4409d7d

12 files changed

+739
-32
lines changed

angular.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
"projectType": "library",
104104
"root": "projects/ngx-diff",
105105
"sourceRoot": "projects/ngx-diff/src",
106-
"prefix": "lib",
106+
"prefix": "ngx",
107107
"architect": {
108108
"build": {
109109
"builder": "@angular-devkit/build-angular:ng-packagr",

projects/ngx-diff/.eslintrc.json

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,27 @@
11
{
22
"extends": "../../.eslintrc.json",
3-
"ignorePatterns": [
4-
"!**/*"
5-
],
3+
"ignorePatterns": ["!**/*"],
64
"overrides": [
75
{
8-
"files": [
9-
"*.ts"
10-
],
6+
"files": ["*.ts"],
117
"parserOptions": {
12-
"project": [
13-
"projects/ngx-diff/tsconfig.lib.json",
14-
"projects/ngx-diff/tsconfig.spec.json"
15-
],
8+
"project": ["projects/ngx-diff/tsconfig.lib.json", "projects/ngx-diff/tsconfig.spec.json"],
169
"createDefaultProgram": true
1710
},
1811
"rules": {
1912
"@angular-eslint/component-selector": [
2013
"error",
2114
{
2215
"type": "element",
23-
"prefix": "lib",
16+
"prefix": "ngx",
2417
"style": "kebab-case"
2518
}
2619
],
2720
"@angular-eslint/directive-selector": [
2821
"error",
2922
{
3023
"type": "attribute",
31-
"prefix": "lib",
24+
"prefix": "ngx",
3225
"style": "camelCase"
3326
}
3427
],
@@ -38,17 +31,12 @@
3831
"accessibility": "explicit"
3932
}
4033
],
41-
"arrow-parens": [
42-
"off",
43-
"always"
44-
],
34+
"arrow-parens": ["off", "always"],
4535
"import/order": "off"
4636
}
4737
},
4838
{
49-
"files": [
50-
"*.html"
51-
],
39+
"files": ["*.html"],
5240
"rules": {}
5341
}
5442
]

projects/ngx-diff/src/lib/components/inline-diff/inline-diff.component.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
--ngx-diff-delete-color: #ffd6d6;
1919
--ngx-diff-equal-color: #ffffff;
2020
--ngx-diff-mix-color: #000;
21-
--ngx-diff-light-mix-percentage: 2%;
21+
--ngx-diff-light-mix-percentage: 4%;
2222
--ngx-diff-heavy-mix-percentage: 10%;
2323

2424
--ngx-diff-inserted-background-color: var(--ngx-diff-insert-color);

projects/ngx-diff/src/lib/components/inline-diff/inline-diff.component.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export class InlineDiffComponent implements OnInit, OnChanges {
7070

7171
if (type === LineDiffType.Placeholder) {
7272
this.expandPlaceholder(index, lineDiff);
73+
this.selectedLine = undefined;
7374
}
7475

7576
this.selectedLineChange.emit({
@@ -108,7 +109,7 @@ export class InlineDiffComponent implements OnInit, OnChanges {
108109
type: LineDiffType.Placeholder,
109110
lineNumberInOldText: null,
110111
lineNumberInNewText: null,
111-
line: '...',
112+
line: `... ${remainingSkippedLines.length} hidden lines ...`,
112113
args: {
113114
skippedLines: remainingSkippedLines,
114115
lineInOldText: lineInOldText + prefix.length,
@@ -265,17 +266,19 @@ export class InlineDiffComponent implements OnInit, OnChanges {
265266
// Take the first 'lineContextSize' lines from this diff to provide context for the last diff
266267
this.outputEqualDiffLines(diffLines.slice(0, this.lineContextSize), diffCalculation);
267268

269+
const skippedLines = diffLines.slice(
270+
this.lineContextSize,
271+
diffLines.length - this.lineContextSize,
272+
);
273+
268274
// Output a special line indicating that some content is equal and has been skipped
269275
diffCalculation.lines.push({
270276
type: LineDiffType.Placeholder,
271277
lineNumberInOldText: null,
272278
lineNumberInNewText: null,
273-
line: '...',
279+
line: `... ${skippedLines.length} hidden lines ...`,
274280
args: {
275-
skippedLines: diffLines.slice(
276-
this.lineContextSize,
277-
diffLines.length - this.lineContextSize,
278-
),
281+
skippedLines,
279282
lineInOldText: diffCalculation.lineInOldText,
280283
lineInNewText: diffCalculation.lineInNewText,
281284
},
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<div class="sbs-diff-title-bar">
2+
@if (title) {
3+
<span>{{ title }}</span
4+
>&nbsp;
5+
}
6+
<span class="sbs-diff-summary-lines-added">+++ {{ diffSummary.numLinesAdded }}</span
7+
>&nbsp;
8+
<span class="sbs-diff-summary-lines-removed">--- {{ diffSummary.numLinesRemoved }}</span>
9+
</div>
10+
@if (isContentEqual) {
11+
<div class="sbs-diff-no-changes-text">There are no changes to display.</div>
12+
}
13+
@if (!isContentEqual) {
14+
<div class="sbs-diff">
15+
<!-- before -->
16+
<div class="sbs-diff-margin">
17+
@for (lineDiff of beforeLines; track lineDiff; let idx = $index) {
18+
<div
19+
class="line-selector"
20+
[ngClass]="
21+
idx === selectedLineIndex ? [lineDiff.cssClass, 'selected'] : [lineDiff.cssClass]
22+
"
23+
(click)="selectLine(idx)"
24+
>
25+
<div class="sbs-diff-before">{{ lineDiff.lineNumber | lineNumber }}</div>
26+
</div>
27+
}
28+
<div class="dmp-margin-bottom-spacer"></div>
29+
</div>
30+
<div class="sbs-diff-content">
31+
<div class="sbs-diff-content-wrapper">
32+
@for (lineDiff of beforeLines; track lineDiff; let idx = $index) {
33+
<div
34+
class="line-content"
35+
[ngClass]="
36+
idx === selectedLineIndex ? [lineDiff.cssClass, 'selected'] : [lineDiff.cssClass]
37+
"
38+
>
39+
<div class="sbs-diff-text">{{ lineDiff.line }}</div>
40+
</div>
41+
}
42+
<div class="dmp-margin-bottom-spacer line-content"></div>
43+
</div>
44+
</div>
45+
<!-- after -->
46+
<div class="sbs-diff-margin">
47+
@for (lineDiff of afterLines; track lineDiff; let idx = $index) {
48+
<div
49+
class="line-selector"
50+
[ngClass]="
51+
idx === selectedLineIndex ? [lineDiff.cssClass, 'selected'] : [lineDiff.cssClass]
52+
"
53+
(click)="selectLine(idx)"
54+
>
55+
<div class="sbs-diff-after">{{ lineDiff.lineNumber | lineNumber }}</div>
56+
</div>
57+
}
58+
<div class="dmp-margin-bottom-spacer"></div>
59+
</div>
60+
<div class="sbs-diff-content">
61+
<div class="sbs-diff-content-wrapper">
62+
@for (lineDiff of afterLines; track lineDiff; let idx = $index) {
63+
<div
64+
class="line-content"
65+
[ngClass]="
66+
idx === selectedLineIndex ? [lineDiff.cssClass, 'selected'] : [lineDiff.cssClass]
67+
"
68+
>
69+
<div class="sbs-diff-text">{{ lineDiff.line }}</div>
70+
</div>
71+
}
72+
<div class="dmp-margin-bottom-spacer line-content"></div>
73+
</div>
74+
</div>
75+
</div>
76+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
:host {
2+
--ngx-diff-border-color: #888888;
3+
--ngx-diff-font-size: 0.9rem;
4+
--ngx-diff-font-family: Consolas, Courier, monospace;
5+
--ngx-diff-font-color: #000000;
6+
--ngx-diff-line-number-font-color: #484848;
7+
--ngx-diff-line-number-hover-font-color: #ffffff;
8+
--ngx-diff-selected-border-color: #ff8000;
9+
10+
--ngx-diff-line-number-width: 2rem;
11+
--ngx-diff-border-width: 1px;
12+
--ngx-diff-line-left-padding: 1rem;
13+
--ngx-diff-bottom-spacer-height: 1rem;
14+
--ngx-diff-title-bar-padding: 0.5rem;
15+
--ngx-diff-title-font-weight: 600;
16+
17+
--ngx-diff-insert-color: #d6ffd6;
18+
--ngx-diff-delete-color: #ffd6d6;
19+
--ngx-diff-equal-color: #ffffff;
20+
--ngx-diff-mix-color: #000;
21+
--ngx-diff-light-mix-percentage: 4%;
22+
--ngx-diff-heavy-mix-percentage: 10%;
23+
24+
--ngx-diff-inserted-background-color: var(--ngx-diff-insert-color);
25+
--ngx-diff-deleted-background-color: var(--ngx-diff-delete-color);
26+
--ngx-diff-equal-background-color: var(--ngx-diff-equal-color);
27+
--ngx-diff-margin-background-color: color-mix(in srgb, var(--ngx-diff-equal-color), var(--ngx-diff-mix-color) var(--ngx-diff-light-mix-percentage));
28+
29+
--ngx-diff-insert-color-darker: color-mix(in srgb, var(--ngx-diff-insert-color), var(--ngx-diff-mix-color) var(--ngx-diff-light-mix-percentage));
30+
--ngx-diff-insert-color-darkest: color-mix(in srgb, var(--ngx-diff-insert-color), var(--ngx-diff-mix-color) var(--ngx-diff-heavy-mix-percentage));
31+
32+
--ngx-diff-delete-color-darker: color-mix(in srgb, var(--ngx-diff-delete-color), var(--ngx-diff-mix-color) var(--ngx-diff-light-mix-percentage));
33+
--ngx-diff-delete-color-darkest: color-mix(in srgb, var(--ngx-diff-delete-color), var(--ngx-diff-mix-color) var(--ngx-diff-heavy-mix-percentage));
34+
}
35+
36+
div.sbs-diff-title-bar {
37+
background-color: var(--ngx-diff-margin-background-color);
38+
font-family: var(--ngx-diff-font-family);
39+
font-size: var(--ngx-diff-font-size);
40+
font-weight: var(--ngx-diff-title-font-weight);
41+
padding: var(--ngx-diff-title-bar-padding);
42+
}
43+
44+
div.sbs-diff-no-changes-text {
45+
font-family: var(--ngx-diff-font-family);
46+
font-size: var(--ngx-diff-font-size);
47+
font-weight: var(--ngx-diff-title-font-weight);
48+
padding: var(--ngx-diff-title-bar-padding);
49+
background-color: var(--ngx-diff-equal-background-color);
50+
color: var(--ngx-diff-font-color);
51+
}
52+
53+
.sbs-diff-summary-lines-added {
54+
color: var(--ngx-diff-insert-color-darkest);
55+
}
56+
57+
.sbs-diff-summary-lines-removed {
58+
color: var(--ngx-diff-delete-color-darkest);
59+
}
60+
61+
div.sbs-diff {
62+
display: flex;
63+
flex-direction: row;
64+
border: var(--ngx-diff-border-width) solid var(--ngx-diff-border-color);
65+
font-family: var(--ngx-diff-font-family);
66+
67+
div.sbs-diff-margin:last-of-type {
68+
border-left: var(--ngx-diff-border-width) solid var(--ngx-diff-border-color);
69+
}
70+
}
71+
72+
div.sbs-diff-content {
73+
position: relative;
74+
top: 0px;
75+
left: 0px;
76+
flex-grow: 1;
77+
overflow-x: auto;
78+
overflow-y: hidden;
79+
}
80+
81+
div.sbs-diff-content-wrapper {
82+
position: absolute;
83+
top: 0px;
84+
left: 0px;
85+
display: flex;
86+
flex-direction: column;
87+
align-items: stretch;
88+
width: 100%;
89+
}
90+
91+
div.sbs-diff-old {
92+
width: var(--ngx-diff-line-number-width);
93+
text-align: center;
94+
font-size: var(--ngx-diff-font-size);
95+
}
96+
97+
div.sbs-diff-new {
98+
width: var(--ngx-diff-line-number-width);
99+
text-align: center;
100+
border-right: var(--ngx-diff-border-width) solid var(--border-color);
101+
font-size: var(--ngx-diff-font-size);
102+
}
103+
104+
div.sbs-diff-text {
105+
white-space: pre;
106+
padding-left: var(--ngx-diff-line-left-padding);
107+
font-size: var(--ngx-diff-font-size);
108+
color: var(--ngx-diff-font-color);
109+
}
110+
111+
.sbs-diff-equal {
112+
background-color: var(--ngx-diff-margin-background-color);
113+
114+
&.line-content {
115+
background-color: var(--ngx-diff-equal-background-color);
116+
}
117+
}
118+
119+
.sbs-diff-delete {
120+
background-color: var(--ngx-diff-delete-color-darker);
121+
122+
&.line-content {
123+
background-color: var(--ngx-diff-deleted-background-color);
124+
}
125+
}
126+
127+
.sbs-diff-insert {
128+
background-color: var(--ngx-diff-insert-color-darker);
129+
130+
&.line-content {
131+
background-color: var(--ngx-diff-inserted-background-color);
132+
}
133+
}
134+
135+
.sbs-diff-delete > div {
136+
display: inline-block;
137+
}
138+
139+
.sbs-diff-insert > div {
140+
display: inline-block;
141+
}
142+
143+
.sbs-diff-equal > div {
144+
display: inline-block;
145+
}
146+
147+
.dmp-margin-bottom-spacer {
148+
height: var(--ngx-diff-bottom-spacer-height);
149+
background-color: var(--ngx-diff-margin-background-color);
150+
border-right: var(--ngx-diff-border-width) solid var(--border-color);
151+
152+
&.line-content {
153+
background-color: var(--ngx-diff-equal-background-color);
154+
}
155+
}
156+
157+
.line-selector {
158+
color: var(--ngx-diff-line-number-font-color);
159+
160+
.sbs-diff-before, .sbs-diff-after {
161+
width: var(--ngx-diff-line-number-width);
162+
text-align: center;
163+
}
164+
165+
&:hover {
166+
cursor: pointer;
167+
color: var(--ngx-diff-line-number-hover-font-color);
168+
}
169+
170+
&.selected {
171+
border-top: var(--ngx-diff-border-width) solid var(--ngx-diff-selected-border-color);
172+
border-left: var(--ngx-diff-border-width) solid var(--ngx-diff-selected-border-color);
173+
border-bottom: var(--ngx-diff-border-width) solid var(--ngx-diff-selected-border-color);
174+
}
175+
}
176+
177+
.line-content.selected {
178+
border-top: var(--ngx-diff-border-width) solid var(--ngx-diff-selected-border-color);
179+
border-right: var(--ngx-diff-border-width) solid var(--ngx-diff-selected-border-color);
180+
border-bottom: var(--ngx-diff-border-width) solid var(--ngx-diff-selected-border-color);
181+
}

0 commit comments

Comments
 (0)