code.gitea.io/gitea@v1.21.7/web_src/js/components/PullRequestMergeForm.vue (about) 1 <script> 2 import {SvgIcon} from '../svg.js'; 3 4 const {csrfToken, pageData} = window.config; 5 6 export default { 7 components: {SvgIcon}, 8 data: () => ({ 9 csrfToken, 10 mergeForm: pageData.pullRequestMergeForm, 11 12 mergeTitleFieldValue: '', 13 mergeMessageFieldValue: '', 14 deleteBranchAfterMerge: false, 15 autoMergeWhenSucceed: false, 16 17 mergeStyle: '', 18 mergeStyleDetail: { // dummy only, these values will come from one of the mergeForm.mergeStyles 19 hideMergeMessageTexts: false, 20 textDoMerge: '', 21 mergeTitleFieldText: '', 22 mergeMessageFieldText: '', 23 hideAutoMerge: false, 24 }, 25 mergeStyleAllowedCount: 0, 26 27 showMergeStyleMenu: false, 28 showActionForm: false, 29 }), 30 computed: { 31 mergeButtonStyleClass() { 32 if (this.mergeForm.allOverridableChecksOk) return 'primary'; 33 return this.autoMergeWhenSucceed ? 'primary' : 'red'; 34 }, 35 forceMerge() { 36 return this.mergeForm.canMergeNow && !this.mergeForm.allOverridableChecksOk; 37 }, 38 }, 39 watch: { 40 mergeStyle(val) { 41 this.mergeStyleDetail = this.mergeForm.mergeStyles.find((e) => e.name === val); 42 } 43 }, 44 created() { 45 this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0); 46 47 let mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed && e.name === this.mergeForm.defaultMergeStyle)?.name; 48 if (!mergeStyle) mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed)?.name; 49 this.switchMergeStyle(mergeStyle, !this.mergeForm.canMergeNow); 50 }, 51 mounted() { 52 document.addEventListener('mouseup', this.hideMergeStyleMenu); 53 }, 54 unmounted() { 55 document.removeEventListener('mouseup', this.hideMergeStyleMenu); 56 }, 57 methods: { 58 hideMergeStyleMenu() { 59 this.showMergeStyleMenu = false; 60 }, 61 toggleActionForm(show) { 62 this.showActionForm = show; 63 if (!show) return; 64 this.deleteBranchAfterMerge = this.mergeForm.defaultDeleteBranchAfterMerge; 65 this.mergeTitleFieldValue = this.mergeStyleDetail.mergeTitleFieldText; 66 this.mergeMessageFieldValue = this.mergeStyleDetail.mergeMessageFieldText; 67 }, 68 switchMergeStyle(name, autoMerge = false) { 69 this.mergeStyle = name; 70 this.autoMergeWhenSucceed = autoMerge; 71 }, 72 clearMergeMessage() { 73 this.mergeMessageFieldValue = this.mergeForm.defaultMergeMessage; 74 }, 75 }, 76 }; 77 </script> 78 <template> 79 <!-- 80 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 81 if the user is a writer and can't do a merge now (canMergeNow==false), then only show the Auto Merge for them 82 How to test the UI manually: 83 * Method 1: manually set some variables in pull.tmpl, eg: {{$notAllOverridableChecksOk = true}} {{$canMergeNow = false}} 84 * Method 2: make a protected branch, then set state=pending/success : 85 curl -X POST ${root_url}/api/v1/repos/${owner}/${repo}/statuses/${sha} \ 86 -H "accept: application/json" -H "authorization: Basic $base64_auth" -H "Content-Type: application/json" \ 87 -d '{"context": "test/context", "description": "description", "state": "${state}", "target_url": "http://localhost"}' 88 --> 89 <div> 90 <!-- eslint-disable-next-line vue/no-v-html --> 91 <div v-if="mergeForm.hasPendingPullRequestMerge" v-html="mergeForm.hasPendingPullRequestMergeTip" class="ui info message"/> 92 93 <form class="ui form form-fetch-action" v-if="showActionForm" :action="mergeForm.baseLink+'/merge'" method="post"> 94 <input type="hidden" name="_csrf" :value="csrfToken"> 95 <input type="hidden" name="head_commit_id" v-model="mergeForm.pullHeadCommitID"> 96 <input type="hidden" name="merge_when_checks_succeed" v-model="autoMergeWhenSucceed"> 97 <input type="hidden" name="force_merge" v-model="forceMerge"> 98 99 <template v-if="!mergeStyleDetail.hideMergeMessageTexts"> 100 <div class="field"> 101 <input type="text" name="merge_title_field" v-model="mergeTitleFieldValue"> 102 </div> 103 <div class="field"> 104 <textarea name="merge_message_field" rows="5" :placeholder="mergeForm.mergeMessageFieldPlaceHolder" v-model="mergeMessageFieldValue"/> 105 <template v-if="mergeMessageFieldValue !== mergeForm.defaultMergeMessage"> 106 <button @click.prevent="clearMergeMessage" class="btn gt-mt-2 gt-p-2 interact-fg" :data-tooltip-content="mergeForm.textClearMergeMessageHint"> 107 {{ mergeForm.textClearMergeMessage }} 108 </button> 109 </template> 110 </div> 111 </template> 112 113 <div class="field" v-if="mergeStyle === 'manually-merged'"> 114 <input type="text" name="merge_commit_id" :placeholder="mergeForm.textMergeCommitId"> 115 </div> 116 117 <button class="ui button" :class="mergeButtonStyleClass" type="submit" name="do" :value="mergeStyle"> 118 {{ mergeStyleDetail.textDoMerge }} 119 <template v-if="autoMergeWhenSucceed"> 120 {{ mergeForm.textAutoMergeButtonWhenSucceed }} 121 </template> 122 </button> 123 124 <button class="ui button merge-cancel" @click="toggleActionForm(false)"> 125 {{ mergeForm.textCancel }} 126 </button> 127 128 <div class="ui checkbox gt-ml-2" v-if="mergeForm.isPullBranchDeletable && !autoMergeWhenSucceed"> 129 <input name="delete_branch_after_merge" type="checkbox" v-model="deleteBranchAfterMerge" id="delete-branch-after-merge"> 130 <label for="delete-branch-after-merge">{{ mergeForm.textDeleteBranch }}</label> 131 </div> 132 </form> 133 134 <div v-if="!showActionForm" class="gt-df"> 135 <!-- the merge button --> 136 <div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? 'grey' : mergeForm.allOverridableChecksOk ? 'primary' : 'red']" @click="toggleActionForm(true)"> 137 <button class="ui button"> 138 <svg-icon name="octicon-git-merge"/> 139 <span class="button-text"> 140 {{ mergeStyleDetail.textDoMerge }} 141 <template v-if="autoMergeWhenSucceed"> 142 {{ mergeForm.textAutoMergeButtonWhenSucceed }} 143 </template> 144 </span> 145 </button> 146 <div class="ui dropdown icon button" @click.stop="showMergeStyleMenu = !showMergeStyleMenu" v-if="mergeStyleAllowedCount>1"> 147 <svg-icon name="octicon-triangle-down" :size="14"/> 148 <div class="menu" :class="{'show':showMergeStyleMenu}"> 149 <template v-for="msd in mergeForm.mergeStyles"> 150 <!-- if can merge now, show one action "merge now", and an action "auto merge when succeed" --> 151 <div class="item" v-if="msd.allowed && mergeForm.canMergeNow" :key="msd.name" @click.stop="switchMergeStyle(msd.name)"> 152 <div class="action-text"> 153 {{ msd.textDoMerge }} 154 </div> 155 <div v-if="!msd.hideAutoMerge" class="auto-merge-small" @click.stop="switchMergeStyle(msd.name, true)"> 156 <svg-icon name="octicon-clock" :size="14"/> 157 <div class="auto-merge-tip"> 158 {{ mergeForm.textAutoMergeWhenSucceed }} 159 </div> 160 </div> 161 </div> 162 163 <!-- if can NOT merge now, only show one action "auto merge when succeed" --> 164 <div class="item" v-if="msd.allowed && !mergeForm.canMergeNow && !msd.hideAutoMerge" :key="msd.name" @click.stop="switchMergeStyle(msd.name, true)"> 165 <div class="action-text"> 166 {{ msd.textDoMerge }} {{ mergeForm.textAutoMergeButtonWhenSucceed }} 167 </div> 168 </div> 169 </template> 170 </div> 171 </div> 172 </div> 173 174 <!-- the cancel auto merge button --> 175 <form v-if="mergeForm.hasPendingPullRequestMerge" :action="mergeForm.baseLink+'/cancel_auto_merge'" method="post" class="gt-ml-4"> 176 <input type="hidden" name="_csrf" :value="csrfToken"> 177 <button class="ui button"> 178 {{ mergeForm.textAutoMergeCancelSchedule }} 179 </button> 180 </form> 181 </div> 182 </div> 183 </template> 184 <style scoped> 185 /* 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 */ 186 .ui.dropdown .menu.show { 187 display: block; 188 } 189 .ui.checkbox label { 190 cursor: pointer; 191 } 192 193 /* make the dropdown list left-aligned */ 194 .ui.merge-button { 195 position: relative; 196 } 197 .ui.merge-button .ui.dropdown { 198 position: static; 199 } 200 .ui.merge-button > .ui.dropdown:last-child > .menu:not(.left) { 201 left: 0; 202 right: auto; 203 } 204 .ui.merge-button .ui.dropdown .menu > .item { 205 display: flex; 206 align-items: stretch; 207 padding: 0 !important; /* polluted by semantic.css: .ui.dropdown .menu > .item { !important } */ 208 } 209 210 /* merge style list item */ 211 .action-text { 212 padding: 0.8rem; 213 flex: 1 214 } 215 216 .auto-merge-small { 217 width: 40px; 218 display: flex; 219 align-items: center; 220 justify-content: center; 221 position: relative; 222 } 223 .auto-merge-small .auto-merge-tip { 224 display: none; 225 left: 38px; 226 top: -1px; 227 bottom: -1px; 228 position: absolute; 229 align-items: center; 230 color: var(--color-info-text); 231 background-color: var(--color-info-bg); 232 border: 1px solid var(--color-info-border); 233 border-left: none; 234 padding-right: 1rem; 235 } 236 237 .auto-merge-small:hover { 238 color: var(--color-info-text); 239 background-color: var(--color-info-bg); 240 border: 1px solid var(--color-info-border); 241 } 242 243 .auto-merge-small:hover .auto-merge-tip { 244 display: flex; 245 } 246 247 </style>