@ -1,130 +1,137 @@
< script lang = "ts" >
< script lang = "ts" setup >
import DiffFileTreeItem from './DiffFileTreeItem.vue' ;
import { loadMoreFiles } from '../features/repo-diff.ts' ;
import { toggleElem } from '../utils/dom.ts' ;
import { diffTreeStore } from '../modules/stores.ts' ;
import { setFileFolding } from '../features/file-fold.ts' ;
import { computed , onMounted , onUnmounted } from 'vue' ;
const LOCAL _STORAGE _KEY = 'diff_file_tree_visible' ;
export default {
components : { DiffFileTreeItem } ,
data : ( ) => {
return { store : diffTreeStore ( ) } ;
} ,
computed : {
fileTree ( ) {
const result = [ ] ;
for ( const file of this . store . files ) {
/ / S p l i t f i l e i n t o d i r e c t o r i e s
const splits = file . Name . split ( '/' ) ;
let index = 0 ;
let parent = null ;
let isFile = false ;
for ( const split of splits ) {
index += 1 ;
/ / r e a c h e d t h e e n d
if ( index === splits . length ) {
isFile = true ;
}
let newParent = {
name : split ,
children : [ ] ,
isFile ,
} ;
if ( isFile === true ) {
newParent . file = file ;
}
if ( parent ) {
/ / c h e c k i f t h e f o l d e r a l r e a d y e x i s t s
const existingFolder = parent . children . find (
( x ) => x . name === split ,
) ;
if ( existingFolder ) {
newParent = existingFolder ;
} else {
parent . children . push ( newParent ) ;
}
} else {
const existingFolder = result . find ( ( x ) => x . name === split ) ;
if ( existingFolder ) {
newParent = existingFolder ;
} else {
result . push ( newParent ) ;
}
}
parent = newParent ;
}
const store = diffTreeStore ( ) ;
const fileTree = computed ( ( ) => {
const result = [ ] ;
for ( const file of store . files ) {
/ / S p l i t f i l e i n t o d i r e c t o r i e s
const splits = file . Name . split ( '/' ) ;
let index = 0 ;
let parent = null ;
let isFile = false ;
for ( const split of splits ) {
index += 1 ;
/ / r e a c h e d t h e e n d
if ( index === splits . length ) {
isFile = true ;
}
const mergeChildIfOnlyOneDir = ( entries ) => {
for ( const entry of entries ) {
if ( entry . children ) {
mergeChildIfOnlyOneDir ( entry . children ) ;
}
if ( entry . children . length === 1 && entry . children [ 0 ] . isFile === false ) {
/ / M e r g e i t t o t h e p a r e n t
entry . name = ` ${ entry . name } / ${ entry . children [ 0 ] . name } ` ;
entry . children = entry . children [ 0 ] . children ;
}
}
let newParent = {
name : split ,
children : [ ] ,
isFile ,
} as {
name : string ,
children : any [ ] ,
isFile : boolean ,
file ? : any ,
} ;
/ / M e r g e f o l d e r s w i t h j u s t a f o l d e r a s c h i l d r e n i n o r d e r t o
/ / r e d u c e t h e d e p t h o f o u r t r e e .
mergeChildIfOnlyOneDir ( result ) ;
return result ;
} ,
} ,
mounted ( ) {
/ / D e f a u l t t o t r u e i f u n s e t
this . store . fileTreeIsVisible = localStorage . getItem ( LOCAL _STORAGE _KEY ) !== 'false' ;
document . querySelector ( '.diff-toggle-file-tree-button' ) . addEventListener ( 'click' , this . toggleVisibility ) ;
this . hashChangeListener = ( ) => {
this . store . selectedItem = window . location . hash ;
this . expandSelectedFile ( ) ;
} ;
this . hashChangeListener ( ) ;
window . addEventListener ( 'hashchange' , this . hashChangeListener ) ;
} ,
unmounted ( ) {
document . querySelector ( '.diff-toggle-file-tree-button' ) . removeEventListener ( 'click' , this . toggleVisibility ) ;
window . removeEventListener ( 'hashchange' , this . hashChangeListener ) ;
} ,
methods : {
expandSelectedFile ( ) {
/ / e x p a n d f i l e i f t h e s e l e c t e d f i l e i s f o l d e d
if ( this . store . selectedItem ) {
const box = document . querySelector ( this . store . selectedItem ) ;
const folded = box ? . getAttribute ( 'data-folded' ) === 'true' ;
if ( folded ) setFileFolding ( box , box . querySelector ( '.fold-file' ) , false ) ;
if ( isFile === true ) {
newParent . file = file ;
}
if ( parent ) {
/ / c h e c k i f t h e f o l d e r a l r e a d y e x i s t s
const existingFolder = parent . children . find (
( x ) => x . name === split ,
) ;
if ( existingFolder ) {
newParent = existingFolder ;
} else {
parent . children . push ( newParent ) ;
}
} else {
const existingFolder = result . find ( ( x ) => x . name === split ) ;
if ( existingFolder ) {
newParent = existingFolder ;
} else {
result . push ( newParent ) ;
}
}
parent = newParent ;
}
}
const mergeChildIfOnlyOneDir = ( entries ) => {
for ( const entry of entries ) {
if ( entry . children ) {
mergeChildIfOnlyOneDir ( entry . children ) ;
}
} ,
toggleVisibility ( ) {
this . updateVisibility ( ! this . store . fileTreeIsVisible ) ;
} ,
updateVisibility ( visible ) {
this . store . fileTreeIsVisible = visible ;
localStorage . setItem ( LOCAL _STORAGE _KEY , this . store . fileTreeIsVisible ) ;
this . updateState ( this . store . fileTreeIsVisible ) ;
} ,
updateState ( visible ) {
const btn = document . querySelector ( '.diff-toggle-file-tree-button' ) ;
const [ toShow , toHide ] = btn . querySelectorAll ( '.icon' ) ;
const tree = document . querySelector ( '#diff-file-tree' ) ;
const newTooltip = btn . getAttribute ( visible ? 'data-hide-text' : 'data-show-text' ) ;
btn . setAttribute ( 'data-tooltip-content' , newTooltip ) ;
toggleElem ( tree , visible ) ;
toggleElem ( toShow , ! visible ) ;
toggleElem ( toHide , visible ) ;
} ,
loadMoreData ( ) {
loadMoreFiles ( this . store . linkLoadMore ) ;
} ,
} ,
} ;
if ( entry . children . length === 1 && entry . children [ 0 ] . isFile === false ) {
/ / M e r g e i t t o t h e p a r e n t
entry . name = ` ${ entry . name } / ${ entry . children [ 0 ] . name } ` ;
entry . children = entry . children [ 0 ] . children ;
}
}
} ;
/ / M e r g e f o l d e r s w i t h j u s t a f o l d e r a s c h i l d r e n i n o r d e r t o
/ / r e d u c e t h e d e p t h o f o u r t r e e .
mergeChildIfOnlyOneDir ( result ) ;
return result ;
} ) ;
onMounted ( ( ) => {
/ / D e f a u l t t o t r u e i f u n s e t
store . fileTreeIsVisible = localStorage . getItem ( LOCAL _STORAGE _KEY ) !== 'false' ;
document . querySelector ( '.diff-toggle-file-tree-button' ) . addEventListener ( 'click' , toggleVisibility ) ;
hashChangeListener ( ) ;
window . addEventListener ( 'hashchange' , hashChangeListener ) ;
} ) ;
onUnmounted ( ( ) => {
document . querySelector ( '.diff-toggle-file-tree-button' ) . removeEventListener ( 'click' , toggleVisibility ) ;
window . removeEventListener ( 'hashchange' , hashChangeListener ) ;
} ) ;
function hashChangeListener ( ) {
store . selectedItem = window . location . hash ;
expandSelectedFile ( ) ;
}
function expandSelectedFile ( ) {
/ / e x p a n d f i l e i f t h e s e l e c t e d f i l e i s f o l d e d
if ( store . selectedItem ) {
const box = document . querySelector ( store . selectedItem ) ;
const folded = box ? . getAttribute ( 'data-folded' ) === 'true' ;
if ( folded ) setFileFolding ( box , box . querySelector ( '.fold-file' ) , false ) ;
}
}
function toggleVisibility ( ) {
updateVisibility ( ! store . fileTreeIsVisible ) ;
}
function updateVisibility ( visible ) {
store . fileTreeIsVisible = visible ;
localStorage . setItem ( LOCAL _STORAGE _KEY , store . fileTreeIsVisible ) ;
updateState ( store . fileTreeIsVisible ) ;
}
function updateState ( visible ) {
const btn = document . querySelector ( '.diff-toggle-file-tree-button' ) ;
const [ toShow , toHide ] = btn . querySelectorAll ( '.icon' ) ;
const tree = document . querySelector ( '#diff-file-tree' ) ;
const newTooltip = btn . getAttribute ( visible ? 'data-hide-text' : 'data-show-text' ) ;
btn . setAttribute ( 'data-tooltip-content' , newTooltip ) ;
toggleElem ( tree , visible ) ;
toggleElem ( toShow , ! visible ) ;
toggleElem ( toHide , visible ) ;
}
function loadMoreData ( ) {
loadMoreFiles ( store . linkLoadMore ) ;
}
< / script >
< template >
< div v-if ="store.fileTreeIsVisible" class="diff-file-tree-items" >
<!-- only render the tree if we 're visible. in many cases this is something that doesn' t change very often -- >
@ -134,6 +141,7 @@ export default {
< / div >
< / div >
< / template >
< style scoped >
. diff - file - tree - items {
display : flex ;