Skip to content

Commit 97d142c

Browse files
committed
feat(diff hunks): added ability to move in previous and next diff
1 parent b758e57 commit 97d142c

File tree

2 files changed

+161
-41
lines changed

2 files changed

+161
-41
lines changed

pages/v1/diff.vue

Lines changed: 160 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,27 @@
5050
</template>
5151
</Navbar>
5252
<main>
53-
<section class="flex items-center gap-4 py-4">
53+
<section
54+
class="
55+
flex
56+
items-center
57+
gap-4
58+
px-4
59+
py-2
60+
mb-4
61+
sticky
62+
top-[80px]
63+
dark:bg-gray-700
64+
bg-gray-300
65+
rounded-md
66+
shadow-sm
67+
w-full
68+
z-1
69+
"
70+
>
5471
<div
55-
class="inline-flex items-center gap-1"
5672
id="toggleScrollInSyncSection"
73+
class="inline-flex items-center gap-1"
5774
>
5875
<label
5976
for="toggleScrollInSync"
@@ -69,6 +86,56 @@
6986
@click="toggleInSyncScroll"
7087
/>
7188
</div>
89+
<div id="nextDiffSection" class="inline-flex items-center gap-1">
90+
<button
91+
id="nextDiff"
92+
class="inline-flex items-center justify-center px-1 py-1 text-sm text-gray-600 transition-transform transform bg-gray-300 border border-gray-800 rounded-sm outline-none dark:border-gray-400 dark:text-white dark:bg-gray-800 align-center focus:ring-4 active:scale-y-75"
93+
aria-label="Go to next diff"
94+
type="button"
95+
@click="goToNextDiff"
96+
>
97+
Next diff
98+
<svg
99+
class="w-4 h-4"
100+
fill="none"
101+
stroke="currentColor"
102+
viewBox="0 0 24 24"
103+
xmlns="http://www.w3.org/2000/svg"
104+
>
105+
<path
106+
stroke-linecap="round"
107+
stroke-linejoin="round"
108+
stroke-width="2"
109+
d="M16 17l-4 4m0 0l-4-4m4 4V3"
110+
></path>
111+
</svg>
112+
</button>
113+
</div>
114+
<div id="prevDiffSection" class="inline-flex items-center gap-1">
115+
<button
116+
id="prevDiff"
117+
class="inline-flex items-center justify-center px-1 py-1 text-sm text-gray-600 transition-transform transform bg-gray-300 border border-gray-800 rounded-sm outline-none dark:border-gray-400 dark:text-white dark:bg-gray-800 align-center focus:ring-4 active:scale-y-75"
118+
aria-label="Go to previous diff"
119+
type="button"
120+
@click="goToPreviousDiff"
121+
>
122+
Previous diff
123+
<svg
124+
class="w-4 h-4"
125+
fill="none"
126+
stroke="currentColor"
127+
viewBox="0 0 24 24"
128+
xmlns="http://www.w3.org/2000/svg"
129+
>
130+
<path
131+
stroke-linecap="round"
132+
stroke-linejoin="round"
133+
stroke-width="2"
134+
d="M8 7l4-4m0 0l4 4m-4-4v18"
135+
></path>
136+
</svg>
137+
</button>
138+
</div>
72139
</section>
73140
<section
74141
class="flex items-stretch w-full gap-4 font-mono text-gray-800 dark:text-gray-50"
@@ -78,6 +145,7 @@
78145
{{ lhsLabel }}
79146
</p>
80147
<div
148+
id="lhsDiff"
81149
class="relative flex-1 px-4 py-2 border-2 rounded-md dark:border-gray-500 line-number-gutter min-h-80"
82150
:class="{
83151
'overflow-y-auto max-h-screen--nav': !isSrollInSyncEnabled,
@@ -103,6 +171,7 @@
103171
{{ rhsLabel }}
104172
</p>
105173
<div
174+
id="rhsDiff"
106175
class="relative flex-1 px-4 py-2 border-2 rounded-md dark:border-gray-500 line-number-gutter min-h-80"
107176
:class="{
108177
'overflow-y-auto max-h-screen--nav': !isSrollInSyncEnabled,
@@ -135,6 +204,47 @@ import { undoUrlSafeBase64, escapeHtml } from '../../helpers/utils'
135204
export default {
136205
layout: 'main',
137206
data() {
207+
const _diff = this.$route.hash
208+
if (_diff) {
209+
const gunzip = pako.ungzip(
210+
Buffer.from(undoUrlSafeBase64(_diff), 'base64')
211+
)
212+
const diffData = JSON.parse(Buffer.from(gunzip).toString('utf8'))
213+
const { diff, lhsLabel, rhsLabel } = diffData
214+
this.lhsLabel = lhsLabel
215+
this.rhsLabel = rhsLabel
216+
this.isSrollInSyncEnabled = true
217+
this.lhsDiff = diff
218+
.map((item) => {
219+
const hunkState = item[0]
220+
if (hunkState === -1 || hunkState === 0) {
221+
const className =
222+
hunkState === -1 ? 'isModified bg-red-300 dark:bg-red-500' : ''
223+
return `<span class="break-all inline p-0 m-0 ${className}">${escapeHtml(
224+
item[1]
225+
)}</span>`
226+
}
227+
return false
228+
})
229+
.filter(Boolean)
230+
.join('')
231+
.split('\n')
232+
this.rhsDiff = diff
233+
.map((item) => {
234+
const hunkState = item[0]
235+
if (hunkState === 1 || hunkState === 0) {
236+
const className =
237+
hunkState === 1 ? 'isModified bg-green-400 dark:bg-green-900' : ''
238+
return `<span class="break-all inline p-0 m-0 ${className}">${escapeHtml(
239+
item[1]
240+
)}</span>`
241+
}
242+
return false
243+
})
244+
.filter(Boolean)
245+
.join('')
246+
.split('\n')
247+
}
138248
return {
139249
lhsDiff: this.lhsDiff,
140250
rhsDiff: this.rhsDiff,
@@ -147,43 +257,15 @@ export default {
147257
}
148258
},
149259
async mounted() {
150-
const _diff = this.$route.hash
151-
const gunzip = pako.ungzip(Buffer.from(undoUrlSafeBase64(_diff), 'base64'))
152-
const diffData = JSON.parse(Buffer.from(gunzip).toString('utf8'))
153-
const { diff, lhsLabel, rhsLabel } = diffData
154-
this.lhsLabel = lhsLabel
155-
this.rhsLabel = rhsLabel
156-
this.isSrollInSyncEnabled = true
157-
this.lhsDiff = diff
158-
.map((item) => {
159-
const hunkState = item[0]
160-
if (hunkState === -1 || hunkState === 0) {
161-
const className =
162-
hunkState === -1 ? 'isModified bg-red-300 dark:bg-red-500' : ''
163-
return `<span class="break-all inline p-0 m-0 ${className}">${escapeHtml(
164-
item[1]
165-
)}</span>`
166-
}
167-
return false
168-
})
169-
.filter(Boolean)
170-
.join('')
171-
.split('\n')
172-
this.rhsDiff = diff
173-
.map((item) => {
174-
const hunkState = item[0]
175-
if (hunkState === 1 || hunkState === 0) {
176-
const className =
177-
hunkState === 1 ? 'isModified bg-green-400 dark:bg-green-900' : ''
178-
return `<span class="break-all inline p-0 m-0 ${className}">${escapeHtml(
179-
item[1]
180-
)}</span>`
181-
}
182-
return false
183-
})
184-
.filter(Boolean)
185-
.join('')
186-
.split('\n')
260+
this.treeWalker = document.createTreeWalker(
261+
document.getElementById('lhsDiff'),
262+
NodeFilter.SHOW_ELEMENT,
263+
{
264+
acceptNode: (node) => {
265+
return node.classList.contains('bg-red-200')
266+
},
267+
}
268+
)
187269
const maxLineCount =
188270
this.lhsDiff.length > this.rhsDiff.length
189271
? this.lhsDiff.length
@@ -194,7 +276,6 @@ export default {
194276
)
195277
const { default: Driver } = await import('driver.js')
196278
const driver = new Driver({
197-
// ...driverJSConfig(this.$isDarkMode),
198279
closeBtnText: 'Skip',
199280
className: 'dark:filter dark:invert',
200281
stageBackground: this.isDarkMode
@@ -205,7 +286,6 @@ export default {
205286
'isSkipScrollInSyncTutorial=true; max-age=31536000; path=/;'
206287
},
207288
})
208-
// Define the steps for introduction
209289
if (!this.isSkipScrollInSyncTutorial) {
210290
driver.defineSteps([
211291
{
@@ -215,6 +295,22 @@ export default {
215295
description: 'Now you can choose to scroll both sides in sync.',
216296
},
217297
},
298+
{
299+
element: '#nextDiffSection',
300+
popover: {
301+
title: 'Travel through diff hunks',
302+
description:
303+
'Now you can move between next and previous diff hunks.',
304+
},
305+
},
306+
{
307+
element: '#prevDiffSection',
308+
popover: {
309+
title: 'Travel through diff hunks',
310+
description:
311+
'Now you can move between next and previous diff hunks.',
312+
},
313+
},
218314
])
219315
driver.start()
220316
}
@@ -262,6 +358,26 @@ export default {
262358
toggleInSyncScroll() {
263359
this.isSrollInSyncEnabled = !this.isSrollInSyncEnabled
264360
},
361+
goToNextDiff() {
362+
const currentNode = this.treeWalker.currentNode
363+
const nextNode = this.treeWalker.nextNode()
364+
if (nextNode) {
365+
currentNode.querySelector('p').classList.remove('selected')
366+
nextNode.focus()
367+
nextNode.querySelector('p').classList.add('selected')
368+
nextNode.scrollIntoView()
369+
}
370+
},
371+
goToPreviousDiff() {
372+
const currentNode = this.treeWalker.currentNode
373+
const prevNode = this.treeWalker.previousNode()
374+
if (prevNode) {
375+
currentNode.querySelector('p').classList.remove('selected')
376+
prevNode.focus()
377+
prevNode.querySelector('p').classList.add('selected')
378+
prevNode.scrollIntoView()
379+
}
380+
},
265381
},
266382
}
267383
</script>
@@ -292,6 +408,9 @@ export default {
292408
padding-left: calc(var(--line-number-gutter-width) - 10px);
293409
line-height: 1.65;
294410
@apply relative;
411+
&.selected {
412+
@apply dark:bg-blue-800 bg-blue-200;
413+
}
295414
&:hover {
296415
@apply bg-gray-200 dark:bg-gray-600;
297416
& > span {

styles/global.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ body,
2020
.page-root,
2121
main {
2222
@apply bg-gray-50 dark:bg-gray-900;
23+
scroll-padding-block: 140px;
2324
}
2425

2526
body {

0 commit comments

Comments
 (0)