2024-07-07 18:32:30 +03:00
< script lang = "ts" >
import { SvgIcon } from '../svg.ts' ;
import { GET } from '../modules/fetch.ts' ;
2024-07-25 04:48:51 +03:00
import { generateAriaId } from '../modules/fomantic/base.ts' ;
2023-07-28 22:18:12 +03:00
export default {
components : { SvgIcon } ,
data : ( ) => {
2024-06-10 23:49:33 +03:00
const el = document . querySelector ( '#diff-commit-select' ) ;
2023-07-28 22:18:12 +03:00
return {
menuVisible : false ,
isLoading : false ,
2024-07-25 04:48:51 +03:00
queryParams : el . getAttribute ( 'data-queryparams' ) ,
issueLink : el . getAttribute ( 'data-issuelink' ) ,
2023-08-14 05:16:40 +03:00
locale : {
filter _changes _by _commit : el . getAttribute ( 'data-filter_changes_by_commit' ) ,
2024-12-08 05:58:18 +03:00
} as Record < string , string > ,
2023-07-28 22:18:12 +03:00
commits : [ ] ,
hoverActivated : false ,
2024-03-22 17:06:53 +03:00
lastReviewCommitSha : null ,
2024-07-25 04:48:51 +03:00
uniqueIdMenu : generateAriaId ( ) ,
uniqueIdShowAll : generateAriaId ( ) ,
2023-07-28 22:18:12 +03:00
} ;
} ,
computed : {
commitsSinceLastReview ( ) {
if ( this . lastReviewCommitSha ) {
return this . commits . length - this . commits . findIndex ( ( x ) => x . id === this . lastReviewCommitSha ) - 1 ;
}
return 0 ;
} ,
} ,
mounted ( ) {
document . body . addEventListener ( 'click' , this . onBodyClick ) ;
this . $el . addEventListener ( 'keydown' , this . onKeyDown ) ;
this . $el . addEventListener ( 'keyup' , this . onKeyUp ) ;
} ,
unmounted ( ) {
document . body . removeEventListener ( 'click' , this . onBodyClick ) ;
this . $el . removeEventListener ( 'keydown' , this . onKeyDown ) ;
this . $el . removeEventListener ( 'keyup' , this . onKeyUp ) ;
} ,
methods : {
2024-12-08 05:58:18 +03:00
onBodyClick ( event : MouseEvent ) {
2023-07-28 22:18:12 +03:00
// close this menu on click outside of this element when the dropdown is currently visible opened
if ( this . $el . contains ( event . target ) ) return ;
if ( this . menuVisible ) {
this . toggleMenu ( ) ;
}
} ,
2024-12-08 05:58:18 +03:00
onKeyDown ( event : KeyboardEvent ) {
2023-07-28 22:18:12 +03:00
if ( ! this . menuVisible ) return ;
2024-12-08 05:58:18 +03:00
const item = document . activeElement as HTMLElement ;
2023-07-28 22:18:12 +03:00
if ( ! this . $el . contains ( item ) ) return ;
switch ( event . key ) {
case 'ArrowDown' : // select next element
event . preventDefault ( ) ;
this . focusElem ( item . nextElementSibling , item ) ;
break ;
case 'ArrowUp' : // select previous element
event . preventDefault ( ) ;
this . focusElem ( item . previousElementSibling , item ) ;
break ;
case 'Escape' : // close menu
event . preventDefault ( ) ;
item . tabIndex = - 1 ;
this . toggleMenu ( ) ;
break ;
}
2024-07-25 04:48:51 +03:00
if ( event . key === 'ArrowDown' || event . key === 'ArrowUp' ) {
const item = document . activeElement ; // try to highlight the selected commits
const commitIdx = item ? . matches ( '.item' ) ? item . getAttribute ( 'data-commit-idx' ) : null ;
if ( commitIdx ) this . highlight ( this . commits [ commitIdx ] ) ;
}
2023-07-28 22:18:12 +03:00
} ,
2024-12-08 05:58:18 +03:00
onKeyUp ( event : KeyboardEvent ) {
2023-07-28 22:18:12 +03:00
if ( ! this . menuVisible ) return ;
const item = document . activeElement ;
if ( ! this . $el . contains ( item ) ) return ;
if ( event . key === 'Shift' && this . hoverActivated ) {
// shift is not pressed anymore -> deactivate hovering and reset hovered and selected
this . hoverActivated = false ;
for ( const commit of this . commits ) {
commit . hovered = false ;
commit . selected = false ;
}
}
} ,
highlight ( commit ) {
if ( ! this . hoverActivated ) return ;
const indexSelected = this . commits . findIndex ( ( x ) => x . selected ) ;
const indexCurrentElem = this . commits . findIndex ( ( x ) => x . id === commit . id ) ;
for ( const [ idx , commit ] of this . commits . entries ( ) ) {
commit . hovered = Math . min ( indexSelected , indexCurrentElem ) <= idx && idx <= Math . max ( indexSelected , indexCurrentElem ) ;
}
} ,
/** Focus given element */
2024-12-08 05:58:18 +03:00
focusElem ( elem : HTMLElement , prevElem : HTMLElement ) {
2023-07-28 22:18:12 +03:00
if ( elem ) {
elem . tabIndex = 0 ;
2023-11-06 05:05:24 +03:00
if ( prevElem ) prevElem . tabIndex = - 1 ;
2023-07-28 22:18:12 +03:00
elem . focus ( ) ;
}
} ,
/** Opens our menu, loads commits before opening */
async toggleMenu ( ) {
this . menuVisible = ! this . menuVisible ;
// load our commits when the menu is not yet visible (it'll be toggled after loading)
// and we got no commits
2024-03-25 21:37:55 +03:00
if ( ! this . commits . length && this . menuVisible && ! this . isLoading ) {
2023-07-28 22:18:12 +03:00
this . isLoading = true ;
try {
await this . fetchCommits ( ) ;
} finally {
this . isLoading = false ;
}
}
// set correct tabindex to allow easier navigation
this . $nextTick ( ( ) => {
if ( this . menuVisible ) {
2024-07-25 04:48:51 +03:00
this . focusElem ( this . $refs . showAllChanges , this . $refs . expandBtn ) ;
2023-07-28 22:18:12 +03:00
} else {
2024-07-25 04:48:51 +03:00
this . focusElem ( this . $refs . expandBtn , this . $refs . showAllChanges ) ;
2023-07-28 22:18:12 +03:00
}
} ) ;
} ,
/** Load the commits to show in this dropdown */
async fetchCommits ( ) {
2023-09-19 03:50:30 +03:00
const resp = await GET ( ` ${ this . issueLink } /commits/list ` ) ;
2023-07-28 22:18:12 +03:00
const results = await resp . json ( ) ;
this . commits . push ( ... results . commits . map ( ( x ) => {
x . hovered = false ;
return x ;
} ) ) ;
this . commits . reverse ( ) ;
this . lastReviewCommitSha = results . last _review _commit _sha || null ;
2024-06-18 11:10:30 +03:00
if ( this . lastReviewCommitSha && ! this . commits . some ( ( x ) => x . id === this . lastReviewCommitSha ) ) {
2023-07-28 22:18:12 +03:00
// the lastReviewCommit is not available (probably due to a force push)
// reset the last review commit sha
this . lastReviewCommitSha = null ;
}
Object . assign ( this . locale , results . locale ) ;
} ,
showAllChanges ( ) {
2024-10-31 17:57:40 +03:00
window . location . assign ( ` ${ this . issueLink } /files ${ this . queryParams } ` ) ;
2023-07-28 22:18:12 +03:00
} ,
/** Called when user clicks on since last review */
changesSinceLastReviewClick ( ) {
2024-10-31 17:57:40 +03:00
window . location . assign ( ` ${ this . issueLink } /files/ ${ this . lastReviewCommitSha } .. ${ this . commits . at ( - 1 ) . id } ${ this . queryParams } ` ) ;
2023-07-28 22:18:12 +03:00
} ,
/** Clicking on a single commit opens this specific commit */
2024-12-08 05:58:18 +03:00
commitClicked ( commitId : string , newWindow = false ) {
2023-07-28 22:18:12 +03:00
const url = ` ${ this . issueLink } /commits/ ${ commitId } ${ this . queryParams } ` ;
if ( newWindow ) {
window . open ( url ) ;
} else {
2024-10-31 17:57:40 +03:00
window . location . assign ( url ) ;
2023-07-28 22:18:12 +03:00
}
} ,
/ * *
* When a commit is clicked with shift this enables the range
* selection . Second click ( with shift ) defines the end of the
* range . This opens the diff of this range
* Exception : first commit is the first commit of this PR . Then
* the diff from beginning of PR up to the second clicked commit is
* opened
* /
commitClickedShift ( commit ) {
this . hoverActivated = ! this . hoverActivated ;
commit . selected = true ;
// Second click -> determine our range and open links accordingly
if ( ! this . hoverActivated ) {
// find all selected commits and generate a link
if ( this . commits [ 0 ] . selected ) {
// first commit is selected - generate a short url with only target sha
const lastCommitIdx = this . commits . findLastIndex ( ( x ) => x . selected ) ;
if ( lastCommitIdx === this . commits . length - 1 ) {
// user selected all commits - just show the normal diff page
2024-10-31 17:57:40 +03:00
window . location . assign ( ` ${ this . issueLink } /files ${ this . queryParams } ` ) ;
2023-07-28 22:18:12 +03:00
} else {
2024-10-31 17:57:40 +03:00
window . location . assign ( ` ${ this . issueLink } /files/ ${ this . commits [ lastCommitIdx ] . id } ${ this . queryParams } ` ) ;
2023-07-28 22:18:12 +03:00
}
} else {
const start = this . commits [ this . commits . findIndex ( ( x ) => x . selected ) - 1 ] . id ;
const end = this . commits . findLast ( ( x ) => x . selected ) . id ;
2024-10-31 17:57:40 +03:00
window . location . assign ( ` ${ this . issueLink } /files/ ${ start } .. ${ end } ${ this . queryParams } ` ) ;
2023-07-28 22:18:12 +03:00
}
}
} ,
2024-03-22 17:06:53 +03:00
} ,
2023-07-28 22:18:12 +03:00
} ;
< / script >
2023-09-02 17:59:07 +03:00
< template >
2024-07-25 04:48:51 +03:00
< div class = "ui scrolling dropdown custom diff-commit-selector" >
2023-09-02 17:59:07 +03:00
< button
2024-07-25 04:48:51 +03:00
ref = "expandBtn"
2023-09-02 17:59:07 +03:00
class = "ui basic button"
@ click . stop = "toggleMenu()"
: data - tooltip - content = "locale.filter_changes_by_commit"
aria - haspopup = "true"
: aria - label = "locale.filter_changes_by_commit"
2024-07-25 04:48:51 +03:00
: aria - controls = "uniqueIdMenu"
: aria - activedescendant = "uniqueIdShowAll"
2023-09-02 17:59:07 +03:00
>
< svg -icon name = "octicon-git-commit" / >
< / button >
2024-07-25 04:48:51 +03:00
<!-- this dropdown is not managed by Fomantic UI , so it needs some classes like "transition" explicitly -- >
< div class = "left menu transition" :id ="uniqueIdMenu" : class = "{visible: menuVisible}" v-show ="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'" >
2023-09-02 17:59:07 +03:00
< div class = "loading-indicator is-loading" v -if = " isLoading " / >
2024-07-25 04:48:51 +03:00
< div v-if ="!isLoading" class="item" :id="uniqueIdShowAll" ref="showAllChanges" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()" >
2023-09-02 17:59:07 +03:00
< div class = "gt-ellipsis" >
{ { locale . show _all _commits } }
< / div >
Migrate margin and padding helpers to tailwind (#30043)
This will conclude the refactor of 1:1 class replacements to tailwind,
except `gt-hidden`. Commands ran:
```bash
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-0#tw-$1$2-0#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-1#tw-$1$2-0.5#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-2#tw-$1$2-1#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-3#tw-$1$2-2#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-4#tw-$1$2-4#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-5#tw-$1$2-8#g' {web_src/js,templates,routers,services}/**/*
```
2024-03-24 19:42:49 +03:00
< div class = "gt-ellipsis text light-2 tw-mb-0" >
2023-09-02 17:59:07 +03:00
{ { locale . stats _num _commits } }
< / div >
< / div >
<!-- only show the show changes since last review if there is a review AND we are commits ahead of the last review -- >
< div
2024-07-25 04:48:51 +03:00
v - if = "lastReviewCommitSha != null"
class = "item" role = "menuitem"
2024-03-25 21:37:55 +03:00
: class = "{disabled: !commitsSinceLastReview}"
2023-09-02 17:59:07 +03:00
@ keydown . enter = "changesSinceLastReviewClick()"
@ click = "changesSinceLastReviewClick()"
>
< div class = "gt-ellipsis" >
{ { locale . show _changes _since _your _last _review } }
< / div >
< div class = "gt-ellipsis text light-2" >
{ { commitsSinceLastReview } } commits
< / div >
< / div >
2024-03-18 17:47:05 +03:00
< span v-if ="!isLoading" class="info text light-2" > {{ locale.select_commit_hold_shift_for_range }} < / span >
2024-07-25 04:48:51 +03:00
< template v-for ="(commit, idx) in commits" :key="commit.id" >
2023-09-02 17:59:07 +03:00
< div
2024-07-25 04:48:51 +03:00
class = "item" role = "menuitem"
: class = "{selected: commit.selected, hovered: commit.hovered}"
: data - commit - idx = "idx"
2023-09-02 17:59:07 +03:00
@ keydown . enter . exact = "commitClicked(commit.id)"
@ keydown . enter . shift . exact = "commitClickedShift(commit)"
@ mouseover . shift = "highlight(commit)"
@ click . exact = "commitClicked(commit.id)"
@ click . ctrl . exact = "commitClicked(commit.id, true)"
@ click . meta . exact = "commitClicked(commit.id, true)"
@ click . shift . exact . stop . prevent = "commitClickedShift(commit)"
>
2024-03-24 17:31:35 +03:00
< div class = "tw-flex-1 tw-flex tw-flex-col tw-gap-1" >
2023-09-02 17:59:07 +03:00
< div class = "gt-ellipsis commit-list-summary" >
{ { commit . summary } }
< / div >
< div class = "gt-ellipsis text light-2" >
{ { commit . committer _or _author _name } }
< span class = "text right" >
2024-01-02 04:25:30 +03:00
<!-- TODO : make this respect the PreferredTimestampTense setting -- >
2024-03-16 00:57:53 +03:00
< relative -time prefix = "" :datetime ="commit.time" data -tooltip -content data -tooltip -interactive = " true " > { { commit . time } } < / r e l a t i v e - t i m e >
2023-09-02 17:59:07 +03:00
< / span >
< / div >
< / div >
2024-03-28 11:31:07 +03:00
< div class = "tw-font-mono" >
2023-09-02 17:59:07 +03:00
{ { commit . short _sha } }
< / div >
< / div >
< / template >
< / div >
< / div >
< / template >
2023-07-28 22:18:12 +03:00
< style scoped >
2024-07-25 04:48:51 +03:00
. ui . dropdown . diff - commit - selector . menu {
margin - top : 0.25 em ;
2023-07-28 22:18:12 +03:00
overflow - x : hidden ;
max - height : 450 px ;
}
2024-07-25 04:48:51 +03:00
. ui . dropdown . diff - commit - selector . menu . loading - indicator {
2023-07-28 22:18:12 +03:00
height : 200 px ;
width : 350 px ;
}
2024-07-25 04:48:51 +03:00
. ui . dropdown . diff - commit - selector . menu > . item ,
. ui . dropdown . diff - commit - selector . menu > . info {
display : flex ;
2023-07-28 22:18:12 +03:00
flex - direction : row ;
line - height : 1.4 ;
2024-07-25 04:48:51 +03:00
gap : 0.25 em ;
2023-07-28 22:18:12 +03:00
padding : 7 px 14 px ! important ;
2024-07-25 04:48:51 +03:00
}
. ui . dropdown . diff - commit - selector . menu > . item : not ( : first - child ) ,
. ui . dropdown . diff - commit - selector . menu > . info : not ( : first - child ) {
2024-03-18 17:47:05 +03:00
border - top : 1 px solid var ( -- color - secondary ) ! important ;
2023-07-28 22:18:12 +03:00
}
2024-07-25 04:48:51 +03:00
. ui . dropdown . diff - commit - selector . menu > . item : focus {
background : var ( -- color - active ) ;
}
. ui . dropdown . diff - commit - selector . menu > . item . hovered {
background - color : var ( -- color - small - accent ) ;
}
. ui . dropdown . diff - commit - selector . menu > . item . selected {
background - color : var ( -- color - accent ) ;
2023-07-28 22:18:12 +03:00
}
2024-07-25 04:48:51 +03:00
. ui . dropdown . diff - commit - selector . menu . commit - list - summary {
2023-07-28 22:18:12 +03:00
max - width : min ( 380 px , 96 vw ) ;
}
< / style >