2024-10-28 21:15:05 +01:00
< script lang = "ts" setup >
2025-06-22 15:37:03 +02:00
import { computed , onMounted , onUnmounted , shallowRef , watch } from 'vue' ;
2024-07-07 17:32:30 +02:00
import { SvgIcon } from '../svg.ts' ;
import { toggleElem } from '../utils/dom.ts' ;
2023-09-02 16:59:07 +02:00
2025-12-25 11:33:34 +01:00
const { pageData } = window . config ;
2023-09-02 16:59:07 +02:00
2025-06-22 15:37:03 +02:00
const mergeForm = pageData . pullRequestMergeForm ;
2024-10-28 21:15:05 +01:00
2025-06-22 15:37:03 +02:00
const mergeTitleFieldValue = shallowRef ( '' ) ;
const mergeMessageFieldValue = shallowRef ( '' ) ;
const deleteBranchAfterMerge = shallowRef ( false ) ;
const autoMergeWhenSucceed = shallowRef ( false ) ;
2024-10-28 21:15:05 +01:00
2025-06-22 15:37:03 +02:00
const mergeStyle = shallowRef ( '' ) ;
const mergeStyleDetail = shallowRef ( {
2024-10-28 21:15:05 +01:00
hideMergeMessageTexts : false ,
textDoMerge : '' ,
mergeTitleFieldText : '' ,
mergeMessageFieldText : '' ,
hideAutoMerge : false ,
} ) ;
2025-06-22 15:37:03 +02:00
const mergeStyleAllowedCount = shallowRef ( 0 ) ;
2024-10-28 21:15:05 +01:00
2025-06-22 15:37:03 +02:00
const showMergeStyleMenu = shallowRef ( false ) ;
const showActionForm = shallowRef ( false ) ;
2024-10-28 21:15:05 +01:00
const mergeButtonStyleClass = computed ( ( ) => {
2025-06-22 15:37:03 +02:00
if ( mergeForm . allOverridableChecksOk ) return 'primary' ;
2024-10-28 21:15:05 +01:00
return autoMergeWhenSucceed . value ? 'primary' : 'red' ;
} ) ;
const forceMerge = computed ( ( ) => {
2025-06-22 15:37:03 +02:00
return mergeForm . canMergeNow && ! mergeForm . allOverridableChecksOk ;
2024-10-28 21:15:05 +01:00
} ) ;
watch ( mergeStyle , ( val ) => {
2025-06-22 15:37:03 +02:00
mergeStyleDetail . value = mergeForm . mergeStyles . find ( ( e : any ) => e . name === val ) ;
2024-10-28 21:15:05 +01:00
for ( const elem of document . querySelectorAll ( '[data-pull-merge-style]' ) ) {
toggleElem ( elem , elem . getAttribute ( 'data-pull-merge-style' ) === val ) ;
}
} ) ;
onMounted ( ( ) => {
2025-06-22 15:37:03 +02:00
mergeStyleAllowedCount . value = mergeForm . mergeStyles . reduce ( ( v : any , msd : any ) => v + ( msd . allowed ? 1 : 0 ) , 0 ) ;
2024-10-28 21:15:05 +01:00
2025-06-22 15:37:03 +02:00
let mergeStyle = mergeForm . mergeStyles . find ( ( e : any ) => e . allowed && e . name === mergeForm . defaultMergeStyle ) ? . name ;
if ( ! mergeStyle ) mergeStyle = mergeForm . mergeStyles . find ( ( e : any ) => e . allowed ) ? . name ;
switchMergeStyle ( mergeStyle , ! mergeForm . canMergeNow ) ;
2024-10-28 21:15:05 +01:00
document . addEventListener ( 'mouseup' , hideMergeStyleMenu ) ;
} ) ;
onUnmounted ( ( ) => {
document . removeEventListener ( 'mouseup' , hideMergeStyleMenu ) ;
} ) ;
function hideMergeStyleMenu ( ) {
showMergeStyleMenu . value = false ;
}
function toggleActionForm ( show : boolean ) {
showActionForm . value = show ;
if ( ! show ) return ;
2025-06-22 15:37:03 +02:00
deleteBranchAfterMerge . value = mergeForm . defaultDeleteBranchAfterMerge ;
2024-10-28 21:15:05 +01:00
mergeTitleFieldValue . value = mergeStyleDetail . value . mergeTitleFieldText ;
mergeMessageFieldValue . value = mergeStyleDetail . value . mergeMessageFieldText ;
}
2025-01-15 21:26:17 +01:00
function switchMergeStyle ( name : string , autoMerge = false ) {
2024-10-28 21:15:05 +01:00
mergeStyle . value = name ;
autoMergeWhenSucceed . value = autoMerge ;
}
function clearMergeMessage ( ) {
2025-06-22 15:37:03 +02:00
mergeMessageFieldValue . value = mergeForm . defaultMergeMessage ;
2024-10-28 21:15:05 +01:00
}
2023-09-02 16:59:07 +02:00
< / script >
2024-10-28 21:15:05 +01:00
2022-05-12 21:39:02 +08:00
< template >
2022-06-11 16:44:20 +02:00
<!--
2022-10-09 10:17:01 +03:00
if this component is shown , either the user is an admin ( can do a merge without checks ) , or they are a writer who has the permission to do a merge
if the user is a writer and can 't do a merge now (canMergeNow==false), then only show the Auto Merge for them
2022-06-11 16:44:20 +02:00
How to test the UI manually:
* Method 1: manually set some variables in pull.tmpl, eg: {{$notAllOverridableChecksOk = true}} {{$canMergeNow = false}}
* Method 2: make a protected branch, then set state=pending/success :
curl -X POST ${root_url}/api/v1/repos/${owner}/${repo}/statuses/${sha} \
-H "accept: application/json" -H "authorization: Basic $base64_auth" -H "Content-Type: application/json" \
-d ' { "context" : "test/context" , "description" : "description" , "state" : "${state}" , "target_url" : "http://localhost" } '
-->
2022-05-12 21:39:02 +08:00
<div>
2023-03-14 17:51:20 +08:00
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="mergeForm.hasPendingPullRequestMerge" v-html="mergeForm.hasPendingPullRequestMergeTip" class="ui info message"/>
2022-06-11 16:44:20 +02:00
2024-03-22 19:17:30 +08:00
<!-- another similar form is in pull.tmpl (manual merge)-->
2024-01-14 23:00:47 +01:00
<form class="ui form form-fetch-action" v-if="showActionForm" :action="mergeForm.baseLink+' / merge '" method="post">
<input type="hidden" name="head_commit_id" v-model="mergeForm.pullHeadCommitID">
<input type="hidden" name="merge_when_checks_succeed" v-model="autoMergeWhenSucceed">
<input type="hidden" name="force_merge" v-model="forceMerge">
2022-05-12 21:39:02 +08:00
2024-01-14 23:00:47 +01:00
<template v-if="!mergeStyleDetail.hideMergeMessageTexts">
<div class="field">
<input type="text" name="merge_title_field" v-model="mergeTitleFieldValue">
2023-02-21 18:03:41 +08:00
</div>
2024-01-14 23:00:47 +01:00
<div class="field">
<textarea name="merge_message_field" rows="5" :placeholder="mergeForm.mergeMessageFieldPlaceHolder" v-model="mergeMessageFieldValue"/>
<template v-if="mergeMessageFieldValue !== mergeForm.defaultMergeMessage">
2024-03-24 17:42:49 +01:00
<button @click.prevent="clearMergeMessage" class="btn tw-mt-1 tw-p-1 interact-fg" :data-tooltip-content="mergeForm.textClearMergeMessageHint">
2024-01-14 23:00:47 +01:00
{{ mergeForm.textClearMergeMessage }}
</button>
2022-06-11 16:44:20 +02:00
</template>
2024-01-14 23:00:47 +01:00
</div>
</template>
2022-05-12 21:39:02 +08:00
2024-01-14 23:00:47 +01:00
<div class="field" v-if="mergeStyle === ' manually - merged '">
<input type="text" name="merge_commit_id" :placeholder="mergeForm.textMergeCommitId">
</div>
2022-05-12 21:39:02 +08:00
2024-01-14 23:00:47 +01:00
<button class="ui button" :class="mergeButtonStyleClass" type="submit" name="do" :value="mergeStyle">
{{ mergeStyleDetail.textDoMerge }}
<template v-if="autoMergeWhenSucceed">
{{ mergeForm.textAutoMergeButtonWhenSucceed }}
</template>
</button>
<button class="ui button merge-cancel" @click="toggleActionForm(false)">
{{ mergeForm.textCancel }}
</button>
2025-01-09 11:51:03 -08:00
<div class="ui checkbox tw-ml-1" v-if="mergeForm.isPullBranchDeletable">
2024-01-14 23:00:47 +01:00
<input name="delete_branch_after_merge" type="checkbox" v-model="deleteBranchAfterMerge" id="delete-branch-after-merge">
<label for="delete-branch-after-merge">{{ mergeForm.textDeleteBranch }}</label>
</div>
</form>
2022-05-12 21:39:02 +08:00
2024-03-22 14:45:10 +01:00
<div v-if="!showActionForm" class="tw-flex">
2022-06-11 16:44:20 +02:00
<!-- the merge button -->
2024-04-14 19:53:52 +02:00
<div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? ' ' : mergeForm.allOverridableChecksOk ? ' primary ' : ' red ']" @click="toggleActionForm(true)">
2022-05-12 21:39:02 +08:00
<button class="ui button">
<svg-icon name="octicon-git-merge"/>
2022-06-11 16:44:20 +02:00
<span class="button-text">
{{ mergeStyleDetail.textDoMerge }}
<template v-if="autoMergeWhenSucceed">
{{ mergeForm.textAutoMergeButtonWhenSucceed }}
</template>
</span>
2022-05-12 21:39:02 +08:00
</button>
2024-12-29 19:04:22 -08:00
<div class="ui dropdown icon button" @click.stop="showMergeStyleMenu = !showMergeStyleMenu">
2022-05-12 21:39:02 +08:00
<svg-icon name="octicon-triangle-down" :size="14"/>
<div class="menu" :class="{' show ':showMergeStyleMenu}">
<template v-for="msd in mergeForm.mergeStyles">
2022-06-11 16:44:20 +02:00
<!-- if can merge now, show one action "merge now", and an action "auto merge when succeed" -->
<div class="item" v-if="msd.allowed && mergeForm.canMergeNow" :key="msd.name" @click.stop="switchMergeStyle(msd.name)">
<div class="action-text">
{{ msd.textDoMerge }}
</div>
<div v-if="!msd.hideAutoMerge" class="auto-merge-small" @click.stop="switchMergeStyle(msd.name, true)">
<svg-icon name="octicon-clock" :size="14"/>
<div class="auto-merge-tip">
{{ mergeForm.textAutoMergeWhenSucceed }}
</div>
</div>
</div>
<!-- if can NOT merge now, only show one action "auto merge when succeed" -->
<div class="item" v-if="msd.allowed && !mergeForm.canMergeNow && !msd.hideAutoMerge" :key="msd.name" @click.stop="switchMergeStyle(msd.name, true)">
<div class="action-text">
{{ msd.textDoMerge }} {{ mergeForm.textAutoMergeButtonWhenSucceed }}
</div>
2022-05-12 21:39:02 +08:00
</div>
</template>
</div>
</div>
</div>
2022-06-11 16:44:20 +02:00
<!-- the cancel auto merge button -->
2024-03-24 17:42:49 +01:00
<form v-if="mergeForm.hasPendingPullRequestMerge" :action="mergeForm.baseLink+' / cancel _auto _merge ' " method=" post " class=" tw - ml - 4 ">
2022-06-11 16:44:20 +02:00
<button class=" ui button " >
{ { mergeForm . textAutoMergeCancelSchedule } }
< / button >
< / form >
< / div >
2022-05-12 21:39:02 +08:00
< / div >
< / template >
2024-10-28 21:15:05 +01:00
2022-05-12 21:39:02 +08:00
< style scoped >
/* to keep UI the same, at the moment we are still using some Fomantic UI styles, but we do not use their scripts, so we need to fine tune some styles */
. ui . dropdown . menu . show {
display : block ;
}
. ui . checkbox label {
cursor : pointer ;
}
2022-06-11 16:44:20 +02:00
/* make the dropdown list left-aligned */
. ui . merge - button {
position : relative ;
}
. ui . merge - button . ui . dropdown {
position : static ;
}
. ui . merge - button > . ui . dropdown : last - child > . menu : not ( . left ) {
left : 0 ;
right : auto ;
}
. ui . merge - button . ui . dropdown . menu > . item {
display : flex ;
align - items : stretch ;
padding : 0 ! important ; /* polluted by semantic.css: .ui.dropdown .menu > .item { !important } */
}
/* merge style list item */
. action - text {
padding : 0.8 rem ;
flex : 1
}
. auto - merge - small {
width : 40 px ;
display : flex ;
align - items : center ;
justify - content : center ;
position : relative ;
}
. auto - merge - small . auto - merge - tip {
display : none ;
left : 38 px ;
top : - 1 px ;
bottom : - 1 px ;
position : absolute ;
align - items : center ;
color : var ( -- color - info - text ) ;
background - color : var ( -- color - info - bg ) ;
border : 1 px solid var ( -- color - info - border ) ;
border - left : none ;
padding - right : 1 rem ;
}
. auto - merge - small : hover {
color : var ( -- color - info - text ) ;
background - color : var ( -- color - info - bg ) ;
border : 1 px solid var ( -- color - info - border ) ;
}
. auto - merge - small : hover . auto - merge - tip {
display : flex ;
}
2022-05-12 21:39:02 +08:00
< / style >