@@ -53,6 +53,7 @@ interface Props {
53
53
leftTitle? : string ;
54
54
rightTitle? : string ;
55
55
compareMethod? : " diffChars" | " diffWords" | " diffWordsWithSpace" | " diffLines" | " diffTrimmedLines" | " diffSentences" | " diffCss" ;
56
+ showMaximizeIcon? : boolean ;
56
57
}
57
58
58
59
const props = withDefaults (defineProps <Props >(), {
@@ -63,12 +64,15 @@ const props = withDefaults(defineProps<Props>(), {
63
64
leftTitle: " Previous" ,
64
65
rightTitle: " Current" ,
65
66
compareMethod: " diffLines" ,
67
+ showMaximizeIcon: false ,
66
68
});
67
69
68
70
// Component state
69
71
const lineInformation = ref <LineInformation []>([]);
70
72
const diffLines = ref <number []>([]);
71
73
const expandedBlocks = ref <number []>([]);
74
+ const showMaximizeModal = ref (false );
75
+ const showMaximizeButton = ref (false );
72
76
73
77
// Compute diff when inputs change
74
78
const computeDiff = (): void => {
@@ -259,17 +263,40 @@ watch(
259
263
},
260
264
{ immediate: true }
261
265
);
266
+
267
+ // Handle maximize functionality
268
+ const toggleMaximizeModal = () => {
269
+ showMaximizeModal .value = ! showMaximizeModal .value ;
270
+ };
271
+
272
+ // Handle mouse enter/leave for showing maximize button
273
+ const onDiffMouseEnter = () => {
274
+ if (props .showMaximizeIcon ) {
275
+ showMaximizeButton .value = true ;
276
+ }
277
+ };
278
+
279
+ const onDiffMouseLeave = () => {
280
+ showMaximizeButton .value = false ;
281
+ };
262
282
</script >
263
283
264
284
<template >
265
- <div class =" diff-viewer" >
285
+ <div class =" diff-viewer" @mouseenter = " onDiffMouseEnter " @mouseleave = " onDiffMouseLeave " >
266
286
<div class =" diff-container" :class =" { 'split-view': splitView }" >
267
287
<!-- Headers -->
268
288
<div v-if =" leftTitle || rightTitle" class =" diff-headers" >
269
289
<div class =" diff-header" >{{ leftTitle }}</div >
270
290
<div v-if =" splitView" class =" diff-header" >{{ rightTitle }}</div >
271
291
</div >
272
292
293
+ <!-- Maximize Button -->
294
+ <button v-if =" showMaximizeIcon && showMaximizeButton" @click =" toggleMaximizeModal" class =" maximize-button" title =" Maximize diff view" >
295
+ <svg width =" 14" height =" 14" viewBox =" 0 0 24 24" fill =" none" xmlns =" http://www.w3.org/2000/svg" >
296
+ <path d =" M3 3V9H5V5H9V3H3ZM3 21H9V19H5V15H3V21ZM21 3H15V5H19V9H21V3ZM15 21H21V15H19V19H15V21Z" fill =" currentColor" />
297
+ </svg >
298
+ </button >
299
+
273
300
<!-- Diff content -->
274
301
<div class =" diff-content" >
275
302
<!-- Left side (old) -->
@@ -313,6 +340,72 @@ watch(
313
340
</div >
314
341
</div >
315
342
</div >
343
+
344
+ <!-- Maximize Modal -->
345
+ <div v-if =" showMaximizeModal" class =" maximize-modal" >
346
+ <div class =" maximize-modal-content" >
347
+ <div class =" maximize-modal-toolbar" >
348
+ <span class =" maximize-modal-title" >{{ leftTitle }} vs {{ rightTitle }}</span >
349
+ <button @click =" toggleMaximizeModal" class =" maximize-modal-close" title =" Close" >
350
+ <svg width =" 16" height =" 16" viewBox =" 0 0 24 24" fill =" none" xmlns =" http://www.w3.org/2000/svg" >
351
+ <path d =" M18 6L6 18M6 6L18 18" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" />
352
+ </svg >
353
+ </button >
354
+ </div >
355
+ <div class =" maximize-modal-body" >
356
+ <div class =" diff-container" :class =" { 'split-view': splitView }" >
357
+ <!-- Headers in Modal -->
358
+ <div v-if =" leftTitle || rightTitle" class =" diff-headers" >
359
+ <div class =" diff-header" >{{ leftTitle }}</div >
360
+ <div v-if =" splitView" class =" diff-header" >{{ rightTitle }}</div >
361
+ </div >
362
+
363
+ <!-- Diff content in Modal -->
364
+ <div class =" diff-content" >
365
+ <!-- Left side (old) in Modal -->
366
+ <div v-if =" splitView" class =" diff-column" >
367
+ <div class =" diff-lines" >
368
+ <template v-for =" (item , itemIndex ) in renderDiff " :key =" ` modal-diff-left-${itemIndex } ` " >
369
+ <!-- Code fold indicator -->
370
+ <div v-if =" item.type === 'fold'" class =" diff-fold" >
371
+ <button @click =" onBlockExpand(item.blockNumber)" class =" diff-fold-button" >
372
+ {{ `⟨ Expand ${item.count} lines... ⟩` }}
373
+ </button >
374
+ </div >
375
+
376
+ <!-- Regular line content -->
377
+ <div v-else-if =" item.type === 'line'" :class =" ['diff-line', { 'diff-line-removed': item.lineInfo.left.type === DiffType.REMOVED }]" >
378
+ <span v-if =" !hideLineNumbers" class =" diff-line-number" >{{ item.lineInfo.left.lineNumber }}</span >
379
+ <span class =" diff-line-content" >{{ item.lineInfo.left.value }}</span >
380
+ </div >
381
+ </template >
382
+ </div >
383
+ </div >
384
+
385
+ <!-- Right side (new) in Modal -->
386
+ <div class =" diff-column" >
387
+ <div class =" diff-lines" >
388
+ <template v-for =" (item , itemIndex ) in renderDiff " :key =" ` modal-diff-right-${itemIndex } ` " >
389
+ <!-- Code fold indicator -->
390
+ <div v-if =" item.type === 'fold'" class =" diff-fold" >
391
+ <button @click =" onBlockExpand(item.blockNumber)" class =" diff-fold-button" >
392
+ {{ `⟨ Expand ${item.count} lines... ⟩` }}
393
+ </button >
394
+ </div >
395
+
396
+ <!-- Regular line content -->
397
+ <div v-else-if =" item.type === 'line'" :class =" ['diff-line', { 'diff-line-added': item.lineInfo.right.type === DiffType.ADDED }]" >
398
+ <span v-if =" !hideLineNumbers" class =" diff-line-number" >{{ item.lineInfo.right.lineNumber }}</span >
399
+ <span class =" diff-line-content" >{{ item.lineInfo.right.value }}</span >
400
+ </div >
401
+ </template >
402
+ </div >
403
+ </div >
404
+ </div >
405
+ </div >
406
+ </div >
407
+ </div >
408
+ </div >
316
409
</div >
317
410
</template >
318
411
@@ -322,6 +415,7 @@ watch(
322
415
overflow : hidden ;
323
416
font-family : monospace ;
324
417
font-size : 0.75rem ;
418
+ position : relative ;
325
419
}
326
420
327
421
.diff-container {
@@ -407,4 +501,92 @@ watch(
407
501
.diff-fold-button :hover {
408
502
text-decoration : underline ;
409
503
}
504
+
505
+ /* Maximize button styles */
506
+ .maximize-button {
507
+ position : absolute ;
508
+ top : 6px ;
509
+ right : 6px ;
510
+ z-index : 10 ;
511
+ background-color : rgba (255 , 255 , 255 , 0.7 );
512
+ border : 1px solid #ddd ;
513
+ border-radius : 3px ;
514
+ padding : 4px ;
515
+ cursor : pointer ;
516
+ opacity : 0.6 ;
517
+ transition : opacity 0.2s ease ;
518
+ }
519
+
520
+ .maximize-button :hover {
521
+ opacity : 1 ;
522
+ }
523
+
524
+ /* Modal styles */
525
+ .maximize-modal {
526
+ position : fixed ;
527
+ top : 0 ;
528
+ left : 0 ;
529
+ right : 0 ;
530
+ bottom : 0 ;
531
+ background-color : rgba (0 , 0 , 0 , 0.5 );
532
+ z-index : 1000 ;
533
+ display : flex ;
534
+ justify-content : center ;
535
+ align-items : center ;
536
+ }
537
+
538
+ .maximize-modal-content {
539
+ background-color : white ;
540
+ width : calc (100% - 40px );
541
+ height : calc (100% - 40px );
542
+ border-radius : 4px ;
543
+ overflow : hidden ;
544
+ display : flex ;
545
+ flex-direction : column ;
546
+ box-shadow : 0 4px 8px rgba (0 , 0 , 0 , 0.1 );
547
+ }
548
+
549
+ .maximize-modal-toolbar {
550
+ display : flex ;
551
+ justify-content : space-between ;
552
+ align-items : center ;
553
+ padding : 10px 15px ;
554
+ background-color : #f8f8f8 ;
555
+ border-bottom : 1px solid #ddd ;
556
+ }
557
+
558
+ .maximize-modal-title {
559
+ font-weight : bold ;
560
+ font-size : 16px ;
561
+ }
562
+
563
+ .maximize-modal-close {
564
+ background : none ;
565
+ border : none ;
566
+ cursor : pointer ;
567
+ padding : 5px ;
568
+ display : flex ;
569
+ align-items : center ;
570
+ justify-content : center ;
571
+ color : #666 ;
572
+ }
573
+
574
+ .maximize-modal-close :hover {
575
+ color : #000 ;
576
+ }
577
+
578
+ .maximize-modal-body {
579
+ flex : 1 ;
580
+ overflow : auto ;
581
+ padding : 0 ;
582
+ }
583
+
584
+ .maximize-modal-body .diff-container {
585
+ height : 100% ;
586
+ }
587
+
588
+ .maximize-modal-body .diff-content {
589
+ max-height : calc (100% - 35px );
590
+ overflow : auto ;
591
+ }
410
592
</style >
0 commit comments