mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	Reorder blocks in vue SFCs (#26874)
The [recommended order](https://vuejs.org/guide/scaling-up/sfc.html) for SFC blocks is script -> template -> style, which we were violating because template and script were swapped. I do find script first also easier to read because the imports are on top, letting me immideatly see a component's dependencies. This is a pure cut-paste refactor with some removal of some empty lines. --------- Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
		| @@ -2,17 +2,6 @@ | ||||
|     Please also update the template file above if this vue is modified. | ||||
|     action status accepted: success, skipped, waiting, blocked, running, failure, cancelled, unknown | ||||
| --> | ||||
| <template> | ||||
|   <span class="gt-df gt-ac" :data-tooltip-content="localeStatus" v-if="status"> | ||||
|     <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/> | ||||
|     <SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/> | ||||
|     <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/> | ||||
|     <SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'blocked'"/> | ||||
|     <SvgIcon name="octicon-meter" class="text yellow" :size="size" :class-name="'job-status-rotate ' + className" v-else-if="status === 'running'"/> | ||||
|     <SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else-if="['failure', 'cancelled', 'unknown'].includes(status)"/> | ||||
|   </span> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {SvgIcon} from '../svg.js'; | ||||
|  | ||||
| @@ -38,3 +27,13 @@ export default { | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <span class="gt-df gt-ac" :data-tooltip-content="localeStatus" v-if="status"> | ||||
|     <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/> | ||||
|     <SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/> | ||||
|     <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/> | ||||
|     <SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'blocked'"/> | ||||
|     <SvgIcon name="octicon-meter" class="text yellow" :size="size" :class-name="'job-status-rotate ' + className" v-else-if="status === 'running'"/> | ||||
|     <SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else-if="['failure', 'cancelled', 'unknown'].includes(status)"/> | ||||
|   </span> | ||||
| </template> | ||||
|   | ||||
| @@ -1,17 +1,3 @@ | ||||
| <template> | ||||
|   <div class="total-contributions"> | ||||
|     {{ locale.contributions_in_the_last_12_months }} | ||||
|   </div> | ||||
|   <calendar-heatmap | ||||
|     :locale="locale" | ||||
|     :no-data-text="locale.no_contributions" | ||||
|     :tooltip-unit="locale.contributions" | ||||
|     :end-date="endDate" | ||||
|     :values="values" | ||||
|     :range-color="colorRange" | ||||
|     @day-click="handleDayClick($event)" | ||||
|   /> | ||||
| </template> | ||||
| <script> | ||||
| import {CalendarHeatmap} from 'vue3-calendar-heatmap'; | ||||
|  | ||||
| @@ -67,3 +53,17 @@ export default { | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <div class="total-contributions"> | ||||
|     {{ locale.contributions_in_the_last_12_months }} | ||||
|   </div> | ||||
|   <calendar-heatmap | ||||
|     :locale="locale" | ||||
|     :no-data-text="locale.no_contributions" | ||||
|     :tooltip-unit="locale.contributions" | ||||
|     :end-date="endDate" | ||||
|     :values="values" | ||||
|     :range-color="colorRange" | ||||
|     @day-click="handleDayClick($event)" | ||||
|   /> | ||||
| </template> | ||||
|   | ||||
| @@ -1,28 +1,3 @@ | ||||
| <template> | ||||
|   <div ref="root"> | ||||
|     <div v-if="loading" class="ui active centered inline loader"/> | ||||
|     <div v-if="!loading && issue !== null"> | ||||
|       <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p> | ||||
|       <p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p> | ||||
|       <p>{{ body }}</p> | ||||
|       <div> | ||||
|         <div | ||||
|           v-for="label in labels" | ||||
|           :key="label.name" | ||||
|           class="ui label" | ||||
|           :style="{ color: label.textColor, backgroundColor: label.color }" | ||||
|         > | ||||
|           {{ label.name }} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div v-if="!loading && issue === null"> | ||||
|       <p><small>{{ i18nErrorOccurred }}</small></p> | ||||
|       <p>{{ i18nErrorMessage }}</p> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import $ from 'jquery'; | ||||
| import {SvgIcon} from '../svg.js'; | ||||
| @@ -115,3 +90,27 @@ export default { | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <div ref="root"> | ||||
|     <div v-if="loading" class="ui active centered inline loader"/> | ||||
|     <div v-if="!loading && issue !== null"> | ||||
|       <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p> | ||||
|       <p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p> | ||||
|       <p>{{ body }}</p> | ||||
|       <div> | ||||
|         <div | ||||
|           v-for="label in labels" | ||||
|           :key="label.name" | ||||
|           class="ui label" | ||||
|           :style="{ color: label.textColor, backgroundColor: label.color }" | ||||
|         > | ||||
|           {{ label.name }} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div v-if="!loading && issue === null"> | ||||
|       <p><small>{{ i18nErrorOccurred }}</small></p> | ||||
|       <p>{{ i18nErrorMessage }}</p> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|   | ||||
| @@ -1,152 +1,3 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <div v-if="!isOrganization" class="ui two item menu"> | ||||
|       <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{ textRepository }}</a> | ||||
|       <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textOrganization }}</a> | ||||
|     </div> | ||||
|     <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos"> | ||||
|       <h4 class="ui top attached header gt-df gt-ac"> | ||||
|         <div class="gt-f1 gt-df gt-ac"> | ||||
|           {{ textMyRepos }} | ||||
|           <span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span> | ||||
|         </div> | ||||
|         <a class="gt-df gt-ac muted" :href="subUrl + '/repo/create' + (isOrganization ? '?org=' + organizationId : '')" :data-tooltip-content="textNewRepo"> | ||||
|           <svg-icon name="octicon-plus"/> | ||||
|         </a> | ||||
|       </h4> | ||||
|       <div class="ui attached segment repos-search"> | ||||
|         <div class="ui fluid action left icon input" :class="{loading: isLoading}"> | ||||
|           <input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos"> | ||||
|           <i class="icon"><svg-icon name="octicon-search" :size="16"/></i> | ||||
|           <div class="ui dropdown icon button" :title="textFilter"> | ||||
|             <svg-icon name="octicon-filter" :size="16"/> | ||||
|             <div class="menu"> | ||||
|               <a class="item" @click="toggleArchivedFilter()"> | ||||
|                 <div class="ui checkbox" ref="checkboxArchivedFilter" :title="checkboxArchivedFilterTitle"> | ||||
|                   <!--the "hidden" is necessary to make the checkbox work without Fomantic UI js, | ||||
|                       otherwise if the "input" handles click event for intermediate status, it breaks the internal state--> | ||||
|                   <input type="checkbox" class="hidden" v-bind.prop="checkboxArchivedFilterProps"> | ||||
|                   <label> | ||||
|                     <svg-icon name="octicon-archive" :size="16" class-name="gt-mr-2"/> | ||||
|                     {{ textShowArchived }} | ||||
|                   </label> | ||||
|                 </div> | ||||
|               </a> | ||||
|               <a class="item" @click="togglePrivateFilter()"> | ||||
|                 <div class="ui checkbox" ref="checkboxPrivateFilter" :title="checkboxPrivateFilterTitle"> | ||||
|                   <input type="checkbox" class="hidden" v-bind.prop="checkboxPrivateFilterProps"> | ||||
|                   <label> | ||||
|                     <svg-icon name="octicon-lock" :size="16" class-name="gt-mr-2"/> | ||||
|                     {{ textShowPrivate }} | ||||
|                   </label> | ||||
|                 </div> | ||||
|               </a> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="ui secondary tiny pointing borderless menu center grid repos-filter"> | ||||
|           <a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')"> | ||||
|             {{ textAll }} | ||||
|             <div v-show="reposFilter === 'all'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'sources'}" @click="changeReposFilter('sources')"> | ||||
|             {{ textSources }} | ||||
|             <div v-show="reposFilter === 'sources'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'forks'}" @click="changeReposFilter('forks')"> | ||||
|             {{ textForks }} | ||||
|             <div v-show="reposFilter === 'forks'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled"> | ||||
|             {{ textMirrors }} | ||||
|             <div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')"> | ||||
|             {{ textCollaborative }} | ||||
|             <div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div v-if="repos.length" class="ui attached table segment gt-rounded-bottom"> | ||||
|         <ul class="repo-owner-name-list"> | ||||
|           <li class="gt-df gt-ac gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id"> | ||||
|             <a class="repo-list-link muted" :href="repo.link"> | ||||
|               <svg-icon :name="repoIcon(repo)" :size="16" class-name="repo-list-icon"/> | ||||
|               <div class="text truncate">{{ repo.full_name }}</div> | ||||
|               <div v-if="repo.archived"> | ||||
|                 <svg-icon name="octicon-archive" :size="16"/> | ||||
|               </div> | ||||
|             </a> | ||||
|             <a class="gt-df gt-ac" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link" :data-tooltip-content="repo.locale_latest_commit_status_state"> | ||||
|               <!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl --> | ||||
|               <svg-icon :name="statusIcon(repo.latest_commit_status_state)" :class-name="'gt-ml-3 commit-status icon text ' + statusColor(repo.latest_commit_status_state)" :size="16"/> | ||||
|             </a> | ||||
|           </li> | ||||
|         </ul> | ||||
|         <div v-if="showMoreReposLink" class="center gt-py-3 gt-border-secondary-top"> | ||||
|           <div class="ui borderless pagination menu narrow"> | ||||
|             <a | ||||
|               class="item navigation gt-py-2" :class="{'disabled': page === 1}" | ||||
|               @click="changePage(1)" :title="textFirstPage" | ||||
|             > | ||||
|               <svg-icon name="gitea-double-chevron-left" :size="16" class-name="gt-mr-2"/> | ||||
|             </a> | ||||
|             <a | ||||
|               class="item navigation gt-py-2" :class="{'disabled': page === 1}" | ||||
|               @click="changePage(page - 1)" :title="textPreviousPage" | ||||
|             > | ||||
|               <svg-icon name="octicon-chevron-left" :size="16" clsas-name="gt-mr-2"/> | ||||
|             </a> | ||||
|             <a class="active item gt-py-2">{{ page }}</a> | ||||
|             <a | ||||
|               class="item navigation" :class="{'disabled': page === finalPage}" | ||||
|               @click="changePage(page + 1)" :title="textNextPage" | ||||
|             > | ||||
|               <svg-icon name="octicon-chevron-right" :size="16" class-name="gt-ml-2"/> | ||||
|             </a> | ||||
|             <a | ||||
|               class="item navigation gt-py-2" :class="{'disabled': page === finalPage}" | ||||
|               @click="changePage(finalPage)" :title="textLastPage" | ||||
|             > | ||||
|               <svg-icon name="gitea-double-chevron-right" :size="16" class-name="gt-ml-2"/> | ||||
|             </a> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs"> | ||||
|       <h4 class="ui top attached header gt-df gt-ac"> | ||||
|         <div class="gt-f1 gt-df gt-ac"> | ||||
|           {{ textMyOrgs }} | ||||
|           <span class="ui grey label gt-ml-3">{{ organizationsTotalCount }}</span> | ||||
|         </div> | ||||
|         <a class="gt-df gt-ac muted" v-if="canCreateOrganization" :href="subUrl + '/org/create'" :data-tooltip-content="textNewOrg"> | ||||
|           <svg-icon name="octicon-plus"/> | ||||
|         </a> | ||||
|       </h4> | ||||
|       <div v-if="organizations.length" class="ui attached table segment gt-rounded-bottom"> | ||||
|         <ul class="repo-owner-name-list"> | ||||
|           <li class="gt-df gt-ac gt-py-3" v-for="org in organizations" :key="org.name"> | ||||
|             <a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)"> | ||||
|               <svg-icon name="octicon-organization" :size="16" class-name="repo-list-icon"/> | ||||
|               <div class="text truncate">{{ org.name }}</div> | ||||
|               <div><!-- div to prevent underline of label on hover --> | ||||
|                 <span class="ui tiny basic label" v-if="org.org_visibility !== 'public'"> | ||||
|                   {{ org.org_visibility === 'limited' ? textOrgVisibilityLimited: textOrgVisibilityPrivate }} | ||||
|                 </span> | ||||
|               </div> | ||||
|             </a> | ||||
|             <div class="text light grey gt-df gt-ac gt-ml-3"> | ||||
|               {{ org.num_repos }} | ||||
|               <svg-icon name="octicon-repo" :size="16" class-name="gt-ml-2 gt-mt-1"/> | ||||
|             </div> | ||||
|           </li> | ||||
|         </ul> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {createApp, nextTick} from 'vue'; | ||||
| import $ from 'jquery'; | ||||
| @@ -485,8 +336,155 @@ export function initDashboardRepoList() { | ||||
| } | ||||
|  | ||||
| export default sfc; // activate the IDE's Vue plugin | ||||
|  | ||||
| </script> | ||||
| <template> | ||||
|   <div> | ||||
|     <div v-if="!isOrganization" class="ui two item menu"> | ||||
|       <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{ textRepository }}</a> | ||||
|       <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textOrganization }}</a> | ||||
|     </div> | ||||
|     <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos"> | ||||
|       <h4 class="ui top attached header gt-df gt-ac"> | ||||
|         <div class="gt-f1 gt-df gt-ac"> | ||||
|           {{ textMyRepos }} | ||||
|           <span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span> | ||||
|         </div> | ||||
|         <a class="gt-df gt-ac muted" :href="subUrl + '/repo/create' + (isOrganization ? '?org=' + organizationId : '')" :data-tooltip-content="textNewRepo"> | ||||
|           <svg-icon name="octicon-plus"/> | ||||
|         </a> | ||||
|       </h4> | ||||
|       <div class="ui attached segment repos-search"> | ||||
|         <div class="ui fluid action left icon input" :class="{loading: isLoading}"> | ||||
|           <input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos"> | ||||
|           <i class="icon"><svg-icon name="octicon-search" :size="16"/></i> | ||||
|           <div class="ui dropdown icon button" :title="textFilter"> | ||||
|             <svg-icon name="octicon-filter" :size="16"/> | ||||
|             <div class="menu"> | ||||
|               <a class="item" @click="toggleArchivedFilter()"> | ||||
|                 <div class="ui checkbox" ref="checkboxArchivedFilter" :title="checkboxArchivedFilterTitle"> | ||||
|                   <!--the "hidden" is necessary to make the checkbox work without Fomantic UI js, | ||||
|                       otherwise if the "input" handles click event for intermediate status, it breaks the internal state--> | ||||
|                   <input type="checkbox" class="hidden" v-bind.prop="checkboxArchivedFilterProps"> | ||||
|                   <label> | ||||
|                     <svg-icon name="octicon-archive" :size="16" class-name="gt-mr-2"/> | ||||
|                     {{ textShowArchived }} | ||||
|                   </label> | ||||
|                 </div> | ||||
|               </a> | ||||
|               <a class="item" @click="togglePrivateFilter()"> | ||||
|                 <div class="ui checkbox" ref="checkboxPrivateFilter" :title="checkboxPrivateFilterTitle"> | ||||
|                   <input type="checkbox" class="hidden" v-bind.prop="checkboxPrivateFilterProps"> | ||||
|                   <label> | ||||
|                     <svg-icon name="octicon-lock" :size="16" class-name="gt-mr-2"/> | ||||
|                     {{ textShowPrivate }} | ||||
|                   </label> | ||||
|                 </div> | ||||
|               </a> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="ui secondary tiny pointing borderless menu center grid repos-filter"> | ||||
|           <a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')"> | ||||
|             {{ textAll }} | ||||
|             <div v-show="reposFilter === 'all'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'sources'}" @click="changeReposFilter('sources')"> | ||||
|             {{ textSources }} | ||||
|             <div v-show="reposFilter === 'sources'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'forks'}" @click="changeReposFilter('forks')"> | ||||
|             {{ textForks }} | ||||
|             <div v-show="reposFilter === 'forks'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled"> | ||||
|             {{ textMirrors }} | ||||
|             <div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|           <a class="item" :class="{active: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')"> | ||||
|             {{ textCollaborative }} | ||||
|             <div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">{{ repoTypeCount }}</div> | ||||
|           </a> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div v-if="repos.length" class="ui attached table segment gt-rounded-bottom"> | ||||
|         <ul class="repo-owner-name-list"> | ||||
|           <li class="gt-df gt-ac gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id"> | ||||
|             <a class="repo-list-link muted" :href="repo.link"> | ||||
|               <svg-icon :name="repoIcon(repo)" :size="16" class-name="repo-list-icon"/> | ||||
|               <div class="text truncate">{{ repo.full_name }}</div> | ||||
|               <div v-if="repo.archived"> | ||||
|                 <svg-icon name="octicon-archive" :size="16"/> | ||||
|               </div> | ||||
|             </a> | ||||
|             <a class="gt-df gt-ac" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link" :data-tooltip-content="repo.locale_latest_commit_status_state"> | ||||
|               <!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl --> | ||||
|               <svg-icon :name="statusIcon(repo.latest_commit_status_state)" :class-name="'gt-ml-3 commit-status icon text ' + statusColor(repo.latest_commit_status_state)" :size="16"/> | ||||
|             </a> | ||||
|           </li> | ||||
|         </ul> | ||||
|         <div v-if="showMoreReposLink" class="center gt-py-3 gt-border-secondary-top"> | ||||
|           <div class="ui borderless pagination menu narrow"> | ||||
|             <a | ||||
|               class="item navigation gt-py-2" :class="{'disabled': page === 1}" | ||||
|               @click="changePage(1)" :title="textFirstPage" | ||||
|             > | ||||
|               <svg-icon name="gitea-double-chevron-left" :size="16" class-name="gt-mr-2"/> | ||||
|             </a> | ||||
|             <a | ||||
|               class="item navigation gt-py-2" :class="{'disabled': page === 1}" | ||||
|               @click="changePage(page - 1)" :title="textPreviousPage" | ||||
|             > | ||||
|               <svg-icon name="octicon-chevron-left" :size="16" clsas-name="gt-mr-2"/> | ||||
|             </a> | ||||
|             <a class="active item gt-py-2">{{ page }}</a> | ||||
|             <a | ||||
|               class="item navigation" :class="{'disabled': page === finalPage}" | ||||
|               @click="changePage(page + 1)" :title="textNextPage" | ||||
|             > | ||||
|               <svg-icon name="octicon-chevron-right" :size="16" class-name="gt-ml-2"/> | ||||
|             </a> | ||||
|             <a | ||||
|               class="item navigation gt-py-2" :class="{'disabled': page === finalPage}" | ||||
|               @click="changePage(finalPage)" :title="textLastPage" | ||||
|             > | ||||
|               <svg-icon name="gitea-double-chevron-right" :size="16" class-name="gt-ml-2"/> | ||||
|             </a> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs"> | ||||
|       <h4 class="ui top attached header gt-df gt-ac"> | ||||
|         <div class="gt-f1 gt-df gt-ac"> | ||||
|           {{ textMyOrgs }} | ||||
|           <span class="ui grey label gt-ml-3">{{ organizationsTotalCount }}</span> | ||||
|         </div> | ||||
|         <a class="gt-df gt-ac muted" v-if="canCreateOrganization" :href="subUrl + '/org/create'" :data-tooltip-content="textNewOrg"> | ||||
|           <svg-icon name="octicon-plus"/> | ||||
|         </a> | ||||
|       </h4> | ||||
|       <div v-if="organizations.length" class="ui attached table segment gt-rounded-bottom"> | ||||
|         <ul class="repo-owner-name-list"> | ||||
|           <li class="gt-df gt-ac gt-py-3" v-for="org in organizations" :key="org.name"> | ||||
|             <a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)"> | ||||
|               <svg-icon name="octicon-organization" :size="16" class-name="repo-list-icon"/> | ||||
|               <div class="text truncate">{{ org.name }}</div> | ||||
|               <div><!-- div to prevent underline of label on hover --> | ||||
|                 <span class="ui tiny basic label" v-if="org.org_visibility !== 'public'"> | ||||
|                   {{ org.org_visibility === 'limited' ? textOrgVisibilityLimited: textOrgVisibilityPrivate }} | ||||
|                 </span> | ||||
|               </div> | ||||
|             </a> | ||||
|             <div class="text light grey gt-df gt-ac gt-ml-3"> | ||||
|               {{ org.num_repos }} | ||||
|               <svg-icon name="octicon-repo" :size="16" class-name="gt-ml-2 gt-mt-1"/> | ||||
|             </div> | ||||
|           </li> | ||||
|         </ul> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <style scoped> | ||||
| ul { | ||||
|   list-style: none; | ||||
|   | ||||
| @@ -1,75 +1,3 @@ | ||||
| <template> | ||||
|   <div class="ui scrolling dropdown custom"> | ||||
|     <button | ||||
|       class="ui basic button" | ||||
|       id="diff-commit-list-expand" | ||||
|       @click.stop="toggleMenu()" | ||||
|       :data-tooltip-content="locale.filter_changes_by_commit" | ||||
|       aria-haspopup="true" | ||||
|       aria-controls="diff-commit-selector-menu" | ||||
|       :aria-label="locale.filter_changes_by_commit" | ||||
|       aria-activedescendant="diff-commit-list-show-all" | ||||
|     > | ||||
|       <svg-icon name="octicon-git-commit"/> | ||||
|     </button> | ||||
|     <div class="menu left transition" id="diff-commit-selector-menu" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'"> | ||||
|       <div class="loading-indicator is-loading" v-if="isLoading"/> | ||||
|       <div v-if="!isLoading" class="vertical item gt-df gt-fc gt-gap-2" id="diff-commit-list-show-all" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()"> | ||||
|         <div class="gt-ellipsis"> | ||||
|           {{ locale.show_all_commits }} | ||||
|         </div> | ||||
|         <div class="gt-ellipsis text light-2 gt-mb-0"> | ||||
|           {{ 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 | ||||
|         v-if="lastReviewCommitSha != null" role="menuitem" | ||||
|         class="vertical item gt-df gt-fc gt-gap-2 gt-border-secondary-top" | ||||
|         :class="{disabled: commitsSinceLastReview === 0}" | ||||
|         @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> | ||||
|       <span v-if="!isLoading" class="info gt-border-secondary-top text light-2">{{ locale.select_commit_hold_shift_for_range }}</span> | ||||
|       <template v-for="commit in commits" :key="commit.id"> | ||||
|         <div | ||||
|           class="vertical item gt-df gt-gap-2 gt-border-secondary-top" role="menuitem" | ||||
|           :class="{selection: commit.selected, hovered: commit.hovered}" | ||||
|           @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)" | ||||
|         > | ||||
|           <div class="gt-f1 gt-df gt-fc gt-gap-2"> | ||||
|             <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"> | ||||
|                 <relative-time class="time-since" prefix="" :datetime="commit.time" data-tooltip-content data-tooltip-interactive="true">{{ commit.time }}</relative-time> | ||||
|               </span> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="gt-mono"> | ||||
|             {{ commit.short_sha }} | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {SvgIcon} from '../svg.js'; | ||||
|  | ||||
| @@ -259,6 +187,77 @@ export default { | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <div class="ui scrolling dropdown custom"> | ||||
|     <button | ||||
|       class="ui basic button" | ||||
|       id="diff-commit-list-expand" | ||||
|       @click.stop="toggleMenu()" | ||||
|       :data-tooltip-content="locale.filter_changes_by_commit" | ||||
|       aria-haspopup="true" | ||||
|       aria-controls="diff-commit-selector-menu" | ||||
|       :aria-label="locale.filter_changes_by_commit" | ||||
|       aria-activedescendant="diff-commit-list-show-all" | ||||
|     > | ||||
|       <svg-icon name="octicon-git-commit"/> | ||||
|     </button> | ||||
|     <div class="menu left transition" id="diff-commit-selector-menu" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'"> | ||||
|       <div class="loading-indicator is-loading" v-if="isLoading"/> | ||||
|       <div v-if="!isLoading" class="vertical item gt-df gt-fc gt-gap-2" id="diff-commit-list-show-all" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()"> | ||||
|         <div class="gt-ellipsis"> | ||||
|           {{ locale.show_all_commits }} | ||||
|         </div> | ||||
|         <div class="gt-ellipsis text light-2 gt-mb-0"> | ||||
|           {{ 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 | ||||
|         v-if="lastReviewCommitSha != null" role="menuitem" | ||||
|         class="vertical item gt-df gt-fc gt-gap-2 gt-border-secondary-top" | ||||
|         :class="{disabled: commitsSinceLastReview === 0}" | ||||
|         @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> | ||||
|       <span v-if="!isLoading" class="info gt-border-secondary-top text light-2">{{ locale.select_commit_hold_shift_for_range }}</span> | ||||
|       <template v-for="commit in commits" :key="commit.id"> | ||||
|         <div | ||||
|           class="vertical item gt-df gt-gap-2 gt-border-secondary-top" role="menuitem" | ||||
|           :class="{selection: commit.selected, hovered: commit.hovered}" | ||||
|           @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)" | ||||
|         > | ||||
|           <div class="gt-f1 gt-df gt-fc gt-gap-2"> | ||||
|             <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"> | ||||
|                 <relative-time class="time-since" prefix="" :datetime="commit.time" data-tooltip-content data-tooltip-interactive="true">{{ commit.time }}</relative-time> | ||||
|               </span> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="gt-mono"> | ||||
|             {{ commit.short_sha }} | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <style scoped> | ||||
|   .hovered:not(.selection) { | ||||
|     background-color: var(--color-small-accent) !important; | ||||
|   | ||||
| @@ -1,25 +1,3 @@ | ||||
| <template> | ||||
|   <ol class="diff-detail-box diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible"> | ||||
|     <li v-for="file in store.files" :key="file.NameHash"> | ||||
|       <div class="gt-font-semibold gt-df gt-ac pull-right"> | ||||
|         <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span> | ||||
|         {{ file.IsBin ? '' : file.Addition + file.Deletion }} | ||||
|         <span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)"> | ||||
|           <div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }"/> | ||||
|         </span> | ||||
|       </div> | ||||
|       <!-- todo finish all file status, now modify, add, delete and rename --> | ||||
|       <span :class="['status', diffTypeToString(file.Type)]" :data-tooltip-content="diffTypeToString(file.Type)"> </span> | ||||
|       <a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a> | ||||
|     </li> | ||||
|     <li v-if="store.isIncomplete" class="gt-pt-2"> | ||||
|       <span class="file gt-df gt-ac gt-sb">{{ store.tooManyFilesMessage }} | ||||
|         <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a> | ||||
|       </span> | ||||
|     </li> | ||||
|   </ol> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {loadMoreFiles} from '../features/repo-diff.js'; | ||||
| import {diffTreeStore} from '../modules/stores.js'; | ||||
| @@ -57,3 +35,24 @@ export default { | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <ol class="diff-detail-box diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible"> | ||||
|     <li v-for="file in store.files" :key="file.NameHash"> | ||||
|       <div class="gt-font-semibold gt-df gt-ac pull-right"> | ||||
|         <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span> | ||||
|         {{ file.IsBin ? '' : file.Addition + file.Deletion }} | ||||
|         <span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)"> | ||||
|           <div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }"/> | ||||
|         </span> | ||||
|       </div> | ||||
|       <!-- todo finish all file status, now modify, add, delete and rename --> | ||||
|       <span :class="['status', diffTypeToString(file.Type)]" :data-tooltip-content="diffTypeToString(file.Type)"> </span> | ||||
|       <a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a> | ||||
|     </li> | ||||
|     <li v-if="store.isIncomplete" class="gt-pt-2"> | ||||
|       <span class="file gt-df gt-ac gt-sb">{{ store.tooManyFilesMessage }} | ||||
|         <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a> | ||||
|       </span> | ||||
|     </li> | ||||
|   </ol> | ||||
| </template> | ||||
|   | ||||
| @@ -1,13 +1,3 @@ | ||||
| <template> | ||||
|   <div v-if="store.fileTreeIsVisible" class="gt-mr-3 gt-mt-3 diff-detail-box"> | ||||
|     <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often --> | ||||
|     <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/> | ||||
|     <div v-if="store.isIncomplete" class="gt-pt-2"> | ||||
|       <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import DiffFileTreeItem from './DiffFileTreeItem.vue'; | ||||
| import {loadMoreFiles} from '../features/repo-diff.js'; | ||||
| @@ -135,3 +125,12 @@ export default { | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <div v-if="store.fileTreeIsVisible" class="gt-mr-3 gt-mt-3 diff-detail-box"> | ||||
|     <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often --> | ||||
|     <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/> | ||||
|     <div v-if="store.isIncomplete" class="gt-pt-2"> | ||||
|       <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|   | ||||
| @@ -1,27 +1,3 @@ | ||||
| <template> | ||||
|   <!--title instead of tooltip above as the tooltip needs too much work with the current methods, i.e. not being loaded or staying open for "too long"--> | ||||
|   <a | ||||
|     v-if="item.isFile" class="item-file" | ||||
|     :class="{'selected': store.selectedItem === '#diff-' + item.file.NameHash, 'viewed': item.file.IsViewed}" | ||||
|     :title="item.name" :href="'#diff-' + item.file.NameHash" | ||||
|   > | ||||
|     <!-- file --> | ||||
|     <SvgIcon name="octicon-file"/> | ||||
|     <span class="gt-ellipsis gt-f1">{{ item.name }}</span> | ||||
|     <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/> | ||||
|   </a> | ||||
|   <div v-else class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed"> | ||||
|     <!-- directory --> | ||||
|     <SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/> | ||||
|     <SvgIcon class="text primary" name="octicon-file-directory-fill"/> | ||||
|     <span class="gt-ellipsis">{{ item.name }}</span> | ||||
|   </div> | ||||
|  | ||||
|   <div v-if="item.children?.length" v-show="!collapsed" class="sub-items"> | ||||
|     <DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem"/> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {SvgIcon} from '../svg.js'; | ||||
| import {diffTreeStore} from '../modules/stores.js'; | ||||
| @@ -52,7 +28,29 @@ export default { | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <!--title instead of tooltip above as the tooltip needs too much work with the current methods, i.e. not being loaded or staying open for "too long"--> | ||||
|   <a | ||||
|     v-if="item.isFile" class="item-file" | ||||
|     :class="{'selected': store.selectedItem === '#diff-' + item.file.NameHash, 'viewed': item.file.IsViewed}" | ||||
|     :title="item.name" :href="'#diff-' + item.file.NameHash" | ||||
|   > | ||||
|     <!-- file --> | ||||
|     <SvgIcon name="octicon-file"/> | ||||
|     <span class="gt-ellipsis gt-f1">{{ item.name }}</span> | ||||
|     <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/> | ||||
|   </a> | ||||
|   <div v-else class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed"> | ||||
|     <!-- directory --> | ||||
|     <SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/> | ||||
|     <SvgIcon class="text primary" name="octicon-file-directory-fill"/> | ||||
|     <span class="gt-ellipsis">{{ item.name }}</span> | ||||
|   </div> | ||||
|  | ||||
|   <div v-if="item.children?.length" v-show="!collapsed" class="sub-items"> | ||||
|     <DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem"/> | ||||
|   </div> | ||||
| </template> | ||||
| <style scoped> | ||||
| a, a:hover { | ||||
|   text-decoration: none; | ||||
|   | ||||
| @@ -1,3 +1,80 @@ | ||||
| <script> | ||||
| import {SvgIcon} from '../svg.js'; | ||||
|  | ||||
| const {csrfToken, pageData} = window.config; | ||||
|  | ||||
| export default { | ||||
|   components: {SvgIcon}, | ||||
|   data: () => ({ | ||||
|     csrfToken, | ||||
|     mergeForm: pageData.pullRequestMergeForm, | ||||
|  | ||||
|     mergeTitleFieldValue: '', | ||||
|     mergeMessageFieldValue: '', | ||||
|     deleteBranchAfterMerge: false, | ||||
|     autoMergeWhenSucceed: false, | ||||
|  | ||||
|     mergeStyle: '', | ||||
|     mergeStyleDetail: { // dummy only, these values will come from one of the mergeForm.mergeStyles | ||||
|       hideMergeMessageTexts: false, | ||||
|       textDoMerge: '', | ||||
|       mergeTitleFieldText: '', | ||||
|       mergeMessageFieldText: '', | ||||
|       hideAutoMerge: false, | ||||
|     }, | ||||
|     mergeStyleAllowedCount: 0, | ||||
|  | ||||
|     showMergeStyleMenu: false, | ||||
|     showActionForm: false, | ||||
|   }), | ||||
|   computed: { | ||||
|     mergeButtonStyleClass() { | ||||
|       if (this.mergeForm.allOverridableChecksOk) return 'green'; | ||||
|       return this.autoMergeWhenSucceed ? 'blue' : 'red'; | ||||
|     }, | ||||
|     forceMerge() { | ||||
|       return this.mergeForm.canMergeNow && !this.mergeForm.allOverridableChecksOk; | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|     mergeStyle(val) { | ||||
|       this.mergeStyleDetail = this.mergeForm.mergeStyles.find((e) => e.name === val); | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0); | ||||
|  | ||||
|     let mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed && e.name === this.mergeForm.defaultMergeStyle)?.name; | ||||
|     if (!mergeStyle) mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed)?.name; | ||||
|     this.switchMergeStyle(mergeStyle, !this.mergeForm.canMergeNow); | ||||
|   }, | ||||
|   mounted() { | ||||
|     document.addEventListener('mouseup', this.hideMergeStyleMenu); | ||||
|   }, | ||||
|   unmounted() { | ||||
|     document.removeEventListener('mouseup', this.hideMergeStyleMenu); | ||||
|   }, | ||||
|   methods: { | ||||
|     hideMergeStyleMenu() { | ||||
|       this.showMergeStyleMenu = false; | ||||
|     }, | ||||
|     toggleActionForm(show) { | ||||
|       this.showActionForm = show; | ||||
|       if (!show) return; | ||||
|       this.deleteBranchAfterMerge = this.mergeForm.defaultDeleteBranchAfterMerge; | ||||
|       this.mergeTitleFieldValue = this.mergeStyleDetail.mergeTitleFieldText; | ||||
|       this.mergeMessageFieldValue = this.mergeStyleDetail.mergeMessageFieldText; | ||||
|     }, | ||||
|     switchMergeStyle(name, autoMerge = false) { | ||||
|       this.mergeStyle = name; | ||||
|       this.autoMergeWhenSucceed = autoMerge; | ||||
|     }, | ||||
|     clearMergeMessage() { | ||||
|       this.mergeMessageFieldValue = this.mergeForm.defaultMergeMessage; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <!-- | ||||
|   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 | ||||
| @@ -106,85 +183,6 @@ | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {SvgIcon} from '../svg.js'; | ||||
|  | ||||
| const {csrfToken, pageData} = window.config; | ||||
|  | ||||
| export default { | ||||
|   components: {SvgIcon}, | ||||
|   data: () => ({ | ||||
|     csrfToken, | ||||
|     mergeForm: pageData.pullRequestMergeForm, | ||||
|  | ||||
|     mergeTitleFieldValue: '', | ||||
|     mergeMessageFieldValue: '', | ||||
|     deleteBranchAfterMerge: false, | ||||
|     autoMergeWhenSucceed: false, | ||||
|  | ||||
|     mergeStyle: '', | ||||
|     mergeStyleDetail: { // dummy only, these values will come from one of the mergeForm.mergeStyles | ||||
|       hideMergeMessageTexts: false, | ||||
|       textDoMerge: '', | ||||
|       mergeTitleFieldText: '', | ||||
|       mergeMessageFieldText: '', | ||||
|       hideAutoMerge: false, | ||||
|     }, | ||||
|     mergeStyleAllowedCount: 0, | ||||
|  | ||||
|     showMergeStyleMenu: false, | ||||
|     showActionForm: false, | ||||
|   }), | ||||
|   computed: { | ||||
|     mergeButtonStyleClass() { | ||||
|       if (this.mergeForm.allOverridableChecksOk) return 'green'; | ||||
|       return this.autoMergeWhenSucceed ? 'blue' : 'red'; | ||||
|     }, | ||||
|     forceMerge() { | ||||
|       return this.mergeForm.canMergeNow && !this.mergeForm.allOverridableChecksOk; | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|     mergeStyle(val) { | ||||
|       this.mergeStyleDetail = this.mergeForm.mergeStyles.find((e) => e.name === val); | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0); | ||||
|  | ||||
|     let mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed && e.name === this.mergeForm.defaultMergeStyle)?.name; | ||||
|     if (!mergeStyle) mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed)?.name; | ||||
|     this.switchMergeStyle(mergeStyle, !this.mergeForm.canMergeNow); | ||||
|   }, | ||||
|   mounted() { | ||||
|     document.addEventListener('mouseup', this.hideMergeStyleMenu); | ||||
|   }, | ||||
|   unmounted() { | ||||
|     document.removeEventListener('mouseup', this.hideMergeStyleMenu); | ||||
|   }, | ||||
|   methods: { | ||||
|     hideMergeStyleMenu() { | ||||
|       this.showMergeStyleMenu = false; | ||||
|     }, | ||||
|     toggleActionForm(show) { | ||||
|       this.showActionForm = show; | ||||
|       if (!show) return; | ||||
|       this.deleteBranchAfterMerge = this.mergeForm.defaultDeleteBranchAfterMerge; | ||||
|       this.mergeTitleFieldValue = this.mergeStyleDetail.mergeTitleFieldText; | ||||
|       this.mergeMessageFieldValue = this.mergeStyleDetail.mergeMessageFieldText; | ||||
|     }, | ||||
|     switchMergeStyle(name, autoMerge = false) { | ||||
|       this.mergeStyle = name; | ||||
|       this.autoMergeWhenSucceed = autoMerge; | ||||
|     }, | ||||
|     clearMergeMessage() { | ||||
|       this.mergeMessageFieldValue = this.mergeForm.defaultMergeMessage; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <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 { | ||||
|   | ||||
| @@ -1,124 +1,3 @@ | ||||
| <template> | ||||
|   <div class="ui container action-view-container"> | ||||
|     <div class="action-view-header"> | ||||
|       <div class="action-info-summary"> | ||||
|         <div class="action-info-summary-title"> | ||||
|           <ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/> | ||||
|           <h2 class="action-info-summary-title-text"> | ||||
|             {{ run.title }} | ||||
|           </h2> | ||||
|         </div> | ||||
|         <button class="ui basic small compact button primary" @click="approveRun()" v-if="run.canApprove"> | ||||
|           {{ locale.approve }} | ||||
|         </button> | ||||
|         <button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel"> | ||||
|           {{ locale.cancel }} | ||||
|         </button> | ||||
|         <button class="ui basic small compact button gt-mr-0 link-action" :data-url="`${run.link}/rerun`" v-else-if="run.canRerun"> | ||||
|           {{ locale.rerun_all }} | ||||
|         </button> | ||||
|       </div> | ||||
|       <div class="action-commit-summary"> | ||||
|         {{ run.commit.localeCommit }} | ||||
|         <a class="muted" :href="run.commit.link">{{ run.commit.shortSHA }}</a> | ||||
|         {{ run.commit.localePushedBy }} | ||||
|         <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a> | ||||
|         <span class="ui label" v-if="run.commit.shortSHA"> | ||||
|           <a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a> | ||||
|         </span> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="action-view-body"> | ||||
|       <div class="action-view-left"> | ||||
|         <div class="job-group-section"> | ||||
|           <div class="job-brief-list"> | ||||
|             <a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1"> | ||||
|               <div class="job-brief-item-left"> | ||||
|                 <ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/> | ||||
|                 <span class="job-brief-name gt-mx-3 gt-ellipsis">{{ job.name }}</span> | ||||
|               </div> | ||||
|               <span class="job-brief-item-right"> | ||||
|                 <SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun gt-mx-3 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun && onHoverRerunIndex === job.id"/> | ||||
|                 <span class="step-summary-duration">{{ job.duration }}</span> | ||||
|               </span> | ||||
|             </a> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="job-artifacts" v-if="artifacts.length > 0"> | ||||
|           <div class="job-artifacts-title"> | ||||
|             {{ locale.artifactsTitle }} | ||||
|           </div> | ||||
|           <ul class="job-artifacts-list"> | ||||
|             <li class="job-artifacts-item" v-for="artifact in artifacts" :key="artifact.name"> | ||||
|               <a class="job-artifacts-link" target="_blank" :href="run.link+'/artifacts/'+artifact.name"> | ||||
|                 <SvgIcon name="octicon-file" class="ui text black job-artifacts-icon"/>{{ artifact.name }} | ||||
|               </a> | ||||
|             </li> | ||||
|           </ul> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="action-view-right"> | ||||
|         <div class="job-info-header"> | ||||
|           <div class="job-info-header-left"> | ||||
|             <h3 class="job-info-header-title"> | ||||
|               {{ currentJob.title }} | ||||
|             </h3> | ||||
|             <p class="job-info-header-detail"> | ||||
|               {{ currentJob.detail }} | ||||
|             </p> | ||||
|           </div> | ||||
|           <div class="job-info-header-right"> | ||||
|             <div class="ui top right pointing dropdown custom jump item" @click.stop="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible"> | ||||
|               <button class="btn gt-interact-bg gt-p-3"> | ||||
|                 <SvgIcon name="octicon-gear" :size="18"/> | ||||
|               </button> | ||||
|               <div class="menu transition action-job-menu" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak> | ||||
|                 <a class="item" @click="toggleTimeDisplay('seconds')"> | ||||
|                   <i class="icon"><SvgIcon :name="timeVisible['log-time-seconds'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i> | ||||
|                   {{ locale.showLogSeconds }} | ||||
|                 </a> | ||||
|                 <a class="item" @click="toggleTimeDisplay('stamp')"> | ||||
|                   <i class="icon"><SvgIcon :name="timeVisible['log-time-stamp'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i> | ||||
|                   {{ locale.showTimeStamps }} | ||||
|                 </a> | ||||
|                 <a class="item" @click="toggleFullScreen()"> | ||||
|                   <i class="icon"><SvgIcon :name="isFullScreen ? 'octicon-check' : 'gitea-empty-checkbox'"/></i> | ||||
|                   {{ locale.showFullScreen }} | ||||
|                 </a> | ||||
|                 <div class="divider"/> | ||||
|                 <a :class="['item', currentJob.steps.length === 0 ? 'disabled' : '']" :href="run.link+'/jobs/'+jobIndex+'/logs'" target="_blank"> | ||||
|                   <i class="icon"><SvgIcon name="octicon-download"/></i> | ||||
|                   {{ locale.downloadLogs }} | ||||
|                 </a> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="job-step-container" ref="steps"> | ||||
|           <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> | ||||
|             <div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''"> | ||||
|               <!-- If the job is done and the job step log is loaded for the first time, show the loading icon | ||||
|                 currentJobStepsStates[i].cursor === null means the log is loaded for the first time | ||||
|               --> | ||||
|               <SvgIcon v-if="isDone(run.status) && currentJobStepsStates[i].expanded && currentJobStepsStates[i].cursor === null" name="octicon-sync" class="gt-mr-3 job-status-rotate"/> | ||||
|               <SvgIcon v-else :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/> | ||||
|               <ActionRunStatus :status="jobStep.status" class="gt-mr-3"/> | ||||
|  | ||||
|               <span class="step-summary-msg gt-ellipsis">{{ jobStep.summary }}</span> | ||||
|               <span class="step-summary-duration">{{ jobStep.duration }}</span> | ||||
|             </div> | ||||
|  | ||||
|             <!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM, | ||||
|             use native DOM elements for "log line" to improve performance, Vue is not suitable for managing so many reactive elements. --> | ||||
|             <div class="job-step-logs" ref="logs" v-show="currentJobStepsStates[i].expanded"/> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {SvgIcon} from '../svg.js'; | ||||
| import ActionRunStatus from './ActionRunStatus.vue'; | ||||
| @@ -472,9 +351,127 @@ export function initRepositoryActionView() { | ||||
|   }); | ||||
|   view.mount(el); | ||||
| } | ||||
|  | ||||
| </script> | ||||
| <template> | ||||
|   <div class="ui container action-view-container"> | ||||
|     <div class="action-view-header"> | ||||
|       <div class="action-info-summary"> | ||||
|         <div class="action-info-summary-title"> | ||||
|           <ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/> | ||||
|           <h2 class="action-info-summary-title-text"> | ||||
|             {{ run.title }} | ||||
|           </h2> | ||||
|         </div> | ||||
|         <button class="ui basic small compact button primary" @click="approveRun()" v-if="run.canApprove"> | ||||
|           {{ locale.approve }} | ||||
|         </button> | ||||
|         <button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel"> | ||||
|           {{ locale.cancel }} | ||||
|         </button> | ||||
|         <button class="ui basic small compact button gt-mr-0 link-action" :data-url="`${run.link}/rerun`" v-else-if="run.canRerun"> | ||||
|           {{ locale.rerun_all }} | ||||
|         </button> | ||||
|       </div> | ||||
|       <div class="action-commit-summary"> | ||||
|         {{ run.commit.localeCommit }} | ||||
|         <a class="muted" :href="run.commit.link">{{ run.commit.shortSHA }}</a> | ||||
|         {{ run.commit.localePushedBy }} | ||||
|         <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a> | ||||
|         <span class="ui label" v-if="run.commit.shortSHA"> | ||||
|           <a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a> | ||||
|         </span> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="action-view-body"> | ||||
|       <div class="action-view-left"> | ||||
|         <div class="job-group-section"> | ||||
|           <div class="job-brief-list"> | ||||
|             <a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1"> | ||||
|               <div class="job-brief-item-left"> | ||||
|                 <ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/> | ||||
|                 <span class="job-brief-name gt-mx-3 gt-ellipsis">{{ job.name }}</span> | ||||
|               </div> | ||||
|               <span class="job-brief-item-right"> | ||||
|                 <SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun gt-mx-3 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun && onHoverRerunIndex === job.id"/> | ||||
|                 <span class="step-summary-duration">{{ job.duration }}</span> | ||||
|               </span> | ||||
|             </a> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="job-artifacts" v-if="artifacts.length > 0"> | ||||
|           <div class="job-artifacts-title"> | ||||
|             {{ locale.artifactsTitle }} | ||||
|           </div> | ||||
|           <ul class="job-artifacts-list"> | ||||
|             <li class="job-artifacts-item" v-for="artifact in artifacts" :key="artifact.name"> | ||||
|               <a class="job-artifacts-link" target="_blank" :href="run.link+'/artifacts/'+artifact.name"> | ||||
|                 <SvgIcon name="octicon-file" class="ui text black job-artifacts-icon"/>{{ artifact.name }} | ||||
|               </a> | ||||
|             </li> | ||||
|           </ul> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="action-view-right"> | ||||
|         <div class="job-info-header"> | ||||
|           <div class="job-info-header-left"> | ||||
|             <h3 class="job-info-header-title"> | ||||
|               {{ currentJob.title }} | ||||
|             </h3> | ||||
|             <p class="job-info-header-detail"> | ||||
|               {{ currentJob.detail }} | ||||
|             </p> | ||||
|           </div> | ||||
|           <div class="job-info-header-right"> | ||||
|             <div class="ui top right pointing dropdown custom jump item" @click.stop="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible"> | ||||
|               <button class="btn gt-interact-bg gt-p-3"> | ||||
|                 <SvgIcon name="octicon-gear" :size="18"/> | ||||
|               </button> | ||||
|               <div class="menu transition action-job-menu" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak> | ||||
|                 <a class="item" @click="toggleTimeDisplay('seconds')"> | ||||
|                   <i class="icon"><SvgIcon :name="timeVisible['log-time-seconds'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i> | ||||
|                   {{ locale.showLogSeconds }} | ||||
|                 </a> | ||||
|                 <a class="item" @click="toggleTimeDisplay('stamp')"> | ||||
|                   <i class="icon"><SvgIcon :name="timeVisible['log-time-stamp'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i> | ||||
|                   {{ locale.showTimeStamps }} | ||||
|                 </a> | ||||
|                 <a class="item" @click="toggleFullScreen()"> | ||||
|                   <i class="icon"><SvgIcon :name="isFullScreen ? 'octicon-check' : 'gitea-empty-checkbox'"/></i> | ||||
|                   {{ locale.showFullScreen }} | ||||
|                 </a> | ||||
|                 <div class="divider"/> | ||||
|                 <a :class="['item', currentJob.steps.length === 0 ? 'disabled' : '']" :href="run.link+'/jobs/'+jobIndex+'/logs'" target="_blank"> | ||||
|                   <i class="icon"><SvgIcon name="octicon-download"/></i> | ||||
|                   {{ locale.downloadLogs }} | ||||
|                 </a> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="job-step-container" ref="steps"> | ||||
|           <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> | ||||
|             <div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''"> | ||||
|               <!-- If the job is done and the job step log is loaded for the first time, show the loading icon | ||||
|                 currentJobStepsStates[i].cursor === null means the log is loaded for the first time | ||||
|               --> | ||||
|               <SvgIcon v-if="isDone(run.status) && currentJobStepsStates[i].expanded && currentJobStepsStates[i].cursor === null" name="octicon-sync" class="gt-mr-3 job-status-rotate"/> | ||||
|               <SvgIcon v-else :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/> | ||||
|               <ActionRunStatus :status="jobStep.status" class="gt-mr-3"/> | ||||
|  | ||||
|               <span class="step-summary-msg gt-ellipsis">{{ jobStep.summary }}</span> | ||||
|               <span class="step-summary-duration">{{ jobStep.duration }}</span> | ||||
|             </div> | ||||
|  | ||||
|             <!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM, | ||||
|             use native DOM elements for "log line" to improve performance, Vue is not suitable for managing so many reactive elements. --> | ||||
|             <div class="job-step-logs" ref="logs" v-show="currentJobStepsStates[i].expanded"/> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <style scoped> | ||||
| .action-view-body { | ||||
|   padding-top: 12px; | ||||
|   | ||||
| @@ -1,54 +1,3 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <div class="activity-bar-graph" ref="style" style="width: 0; height: 0;"/> | ||||
|     <div class="activity-bar-graph-alt" ref="altStyle" style="width: 0; height: 0;"/> | ||||
|     <vue-bar-graph | ||||
|       :points="graphPoints" | ||||
|       :show-x-axis="true" | ||||
|       :show-y-axis="false" | ||||
|       :show-values="true" | ||||
|       :width="graphWidth" | ||||
|       :bar-color="colors.barColor" | ||||
|       :text-color="colors.textColor" | ||||
|       :text-alt-color="colors.textAltColor" | ||||
|       :height="100" | ||||
|       :label-height="20" | ||||
|     > | ||||
|       <template #label="opt"> | ||||
|         <g v-for="(author, idx) in graphAuthors" :key="author.position"> | ||||
|           <a | ||||
|             v-if="opt.bar.index === idx && author.home_link" | ||||
|             :href="author.home_link" | ||||
|           > | ||||
|             <image | ||||
|               :x="`${opt.bar.midPoint - 10}px`" | ||||
|               :y="`${opt.bar.yLabel}px`" | ||||
|               height="20" | ||||
|               width="20" | ||||
|               :href="author.avatar_link" | ||||
|             /> | ||||
|           </a> | ||||
|           <image | ||||
|             v-else-if="opt.bar.index === idx" | ||||
|             :x="`${opt.bar.midPoint - 10}px`" | ||||
|             :y="`${opt.bar.yLabel}px`" | ||||
|             height="20" | ||||
|             width="20" | ||||
|             :href="author.avatar_link" | ||||
|           /> | ||||
|         </g> | ||||
|       </template> | ||||
|       <template #title="opt"> | ||||
|         <tspan v-for="(author, idx) in graphAuthors" :key="author.position"> | ||||
|           <tspan v-if="opt.bar.index === idx"> | ||||
|             {{ author.name }} | ||||
|           </tspan> | ||||
|         </tspan> | ||||
|       </template> | ||||
|     </vue-bar-graph> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import VueBarGraph from 'vue-bar-graph'; | ||||
| import {createApp} from 'vue'; | ||||
| @@ -110,3 +59,53 @@ export function initRepoActivityTopAuthorsChart() { | ||||
|  | ||||
| export default sfc; // activate the IDE's Vue plugin | ||||
| </script> | ||||
| <template> | ||||
|   <div> | ||||
|     <div class="activity-bar-graph" ref="style" style="width: 0; height: 0;"/> | ||||
|     <div class="activity-bar-graph-alt" ref="altStyle" style="width: 0; height: 0;"/> | ||||
|     <vue-bar-graph | ||||
|       :points="graphPoints" | ||||
|       :show-x-axis="true" | ||||
|       :show-y-axis="false" | ||||
|       :show-values="true" | ||||
|       :width="graphWidth" | ||||
|       :bar-color="colors.barColor" | ||||
|       :text-color="colors.textColor" | ||||
|       :text-alt-color="colors.textAltColor" | ||||
|       :height="100" | ||||
|       :label-height="20" | ||||
|     > | ||||
|       <template #label="opt"> | ||||
|         <g v-for="(author, idx) in graphAuthors" :key="author.position"> | ||||
|           <a | ||||
|             v-if="opt.bar.index === idx && author.home_link" | ||||
|             :href="author.home_link" | ||||
|           > | ||||
|             <image | ||||
|               :x="`${opt.bar.midPoint - 10}px`" | ||||
|               :y="`${opt.bar.yLabel}px`" | ||||
|               height="20" | ||||
|               width="20" | ||||
|               :href="author.avatar_link" | ||||
|             /> | ||||
|           </a> | ||||
|           <image | ||||
|             v-else-if="opt.bar.index === idx" | ||||
|             :x="`${opt.bar.midPoint - 10}px`" | ||||
|             :y="`${opt.bar.yLabel}px`" | ||||
|             height="20" | ||||
|             width="20" | ||||
|             :href="author.avatar_link" | ||||
|           /> | ||||
|         </g> | ||||
|       </template> | ||||
|       <template #title="opt"> | ||||
|         <tspan v-for="(author, idx) in graphAuthors" :key="author.position"> | ||||
|           <tspan v-if="opt.bar.index === idx"> | ||||
|             {{ author.name }} | ||||
|           </tspan> | ||||
|         </tspan> | ||||
|       </template> | ||||
|     </vue-bar-graph> | ||||
|   </div> | ||||
| </template> | ||||
|   | ||||
| @@ -1,76 +1,3 @@ | ||||
| <template> | ||||
|   <div class="ui dropdown custom"> | ||||
|     <button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df gt-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible"> | ||||
|       <span class="text gt-df gt-ac gt-mr-2"> | ||||
|         <template v-if="release">{{ textReleaseCompare }}</template> | ||||
|         <template v-else> | ||||
|           <svg-icon v-if="isViewTag" name="octicon-tag"/> | ||||
|           <svg-icon v-else name="octicon-git-branch"/> | ||||
|           <strong ref="dropdownRefName" class="gt-ml-3">{{ refNameText }}</strong> | ||||
|         </template> | ||||
|       </span> | ||||
|       <svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/> | ||||
|     </button> | ||||
|     <div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak> | ||||
|       <div class="ui icon search input"> | ||||
|         <i class="icon"><svg-icon name="octicon-filter" :size="16"/></i> | ||||
|         <input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" :placeholder="searchFieldPlaceholder"> | ||||
|       </div> | ||||
|       <div v-if="showBranchesInDropdown" class="branch-tag-tab"> | ||||
|         <a class="branch-tag-item muted" :class="{active: mode === 'branches'}" href="#" @click="handleTabSwitch('branches')"> | ||||
|           <svg-icon name="octicon-git-branch" :size="16" class-name="gt-mr-2"/>{{ textBranches }} | ||||
|         </a> | ||||
|         <a v-if="!noTag" class="branch-tag-item muted" :class="{active: mode === 'tags'}" href="#" @click="handleTabSwitch('tags')"> | ||||
|           <svg-icon name="octicon-tag" :size="16" class-name="gt-mr-2"/>{{ textTags }} | ||||
|         </a> | ||||
|       </div> | ||||
|       <div class="branch-tag-divider"/> | ||||
|       <div class="scrolling menu" ref="scrollContainer"> | ||||
|         <svg-icon name="octicon-rss" symbol-id="svg-symbol-octicon-rss"/> | ||||
|         <div class="loading-indicator is-loading" v-if="isLoading"/> | ||||
|         <div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index"> | ||||
|           {{ item.name }} | ||||
|           <div class="ui label" v-if="item.name===defaultBranch && mode === 'branches'"> | ||||
|             {{ textDefaultBranchLabel }} | ||||
|           </div> | ||||
|           <a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon gt-float-right" :href="rssURLPrefix + item.url" target="_blank" @click.stop> | ||||
|             <!-- creating a lot of Vue component is pretty slow, so we use a static SVG here --> | ||||
|             <svg width="14" height="14" class="svg octicon-rss"><use href="#svg-symbol-octicon-rss"/></svg> | ||||
|           </a> | ||||
|         </div> | ||||
|         <div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length"> | ||||
|           <a href="#" @click="createNewBranch()"> | ||||
|             <div v-show="shouldCreateTag"> | ||||
|               <i class="reference tags icon"/> | ||||
|               <!-- eslint-disable-next-line vue/no-v-html --> | ||||
|               <span v-html="textCreateTag.replace('%s', searchTerm)"/> | ||||
|             </div> | ||||
|             <div v-show="!shouldCreateTag"> | ||||
|               <svg-icon name="octicon-git-branch"/> | ||||
|               <!-- eslint-disable-next-line vue/no-v-html --> | ||||
|               <span v-html="textCreateBranch.replace('%s', searchTerm)"/> | ||||
|             </div> | ||||
|             <div class="text small"> | ||||
|               <span v-if="isViewBranch || release">{{ textCreateBranchFrom.replace('%s', branchName) }}</span> | ||||
|               <span v-else-if="isViewTag">{{ textCreateBranchFrom.replace('%s', tagName) }}</span> | ||||
|               <span v-else>{{ textCreateBranchFrom.replace('%s', commitIdShort) }}</span> | ||||
|             </div> | ||||
|           </a> | ||||
|           <form ref="newBranchForm" :action="formActionUrl" method="post"> | ||||
|             <input type="hidden" name="_csrf" :value="csrfToken"> | ||||
|             <input type="hidden" name="new_branch_name" v-model="searchTerm"> | ||||
|             <input type="hidden" name="create_tag" v-model="shouldCreateTag"> | ||||
|             <input type="hidden" name="current_path" v-model="treePath" v-if="treePath"> | ||||
|           </form> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="message" v-if="showNoResults && !isLoading"> | ||||
|         {{ noResults }} | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {createApp, nextTick} from 'vue'; | ||||
| import $ from 'jquery'; | ||||
| @@ -317,7 +244,78 @@ export function initRepoBranchTagSelector(selector) { | ||||
|  | ||||
| export default sfc; // activate IDE's Vue plugin | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <div class="ui dropdown custom"> | ||||
|     <button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df gt-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible"> | ||||
|       <span class="text gt-df gt-ac gt-mr-2"> | ||||
|         <template v-if="release">{{ textReleaseCompare }}</template> | ||||
|         <template v-else> | ||||
|           <svg-icon v-if="isViewTag" name="octicon-tag"/> | ||||
|           <svg-icon v-else name="octicon-git-branch"/> | ||||
|           <strong ref="dropdownRefName" class="gt-ml-3">{{ refNameText }}</strong> | ||||
|         </template> | ||||
|       </span> | ||||
|       <svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/> | ||||
|     </button> | ||||
|     <div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak> | ||||
|       <div class="ui icon search input"> | ||||
|         <i class="icon"><svg-icon name="octicon-filter" :size="16"/></i> | ||||
|         <input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" :placeholder="searchFieldPlaceholder"> | ||||
|       </div> | ||||
|       <div v-if="showBranchesInDropdown" class="branch-tag-tab"> | ||||
|         <a class="branch-tag-item muted" :class="{active: mode === 'branches'}" href="#" @click="handleTabSwitch('branches')"> | ||||
|           <svg-icon name="octicon-git-branch" :size="16" class-name="gt-mr-2"/>{{ textBranches }} | ||||
|         </a> | ||||
|         <a v-if="!noTag" class="branch-tag-item muted" :class="{active: mode === 'tags'}" href="#" @click="handleTabSwitch('tags')"> | ||||
|           <svg-icon name="octicon-tag" :size="16" class-name="gt-mr-2"/>{{ textTags }} | ||||
|         </a> | ||||
|       </div> | ||||
|       <div class="branch-tag-divider"/> | ||||
|       <div class="scrolling menu" ref="scrollContainer"> | ||||
|         <svg-icon name="octicon-rss" symbol-id="svg-symbol-octicon-rss"/> | ||||
|         <div class="loading-indicator is-loading" v-if="isLoading"/> | ||||
|         <div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index"> | ||||
|           {{ item.name }} | ||||
|           <div class="ui label" v-if="item.name===defaultBranch && mode === 'branches'"> | ||||
|             {{ textDefaultBranchLabel }} | ||||
|           </div> | ||||
|           <a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon gt-float-right" :href="rssURLPrefix + item.url" target="_blank" @click.stop> | ||||
|             <!-- creating a lot of Vue component is pretty slow, so we use a static SVG here --> | ||||
|             <svg width="14" height="14" class="svg octicon-rss"><use href="#svg-symbol-octicon-rss"/></svg> | ||||
|           </a> | ||||
|         </div> | ||||
|         <div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length"> | ||||
|           <a href="#" @click="createNewBranch()"> | ||||
|             <div v-show="shouldCreateTag"> | ||||
|               <i class="reference tags icon"/> | ||||
|               <!-- eslint-disable-next-line vue/no-v-html --> | ||||
|               <span v-html="textCreateTag.replace('%s', searchTerm)"/> | ||||
|             </div> | ||||
|             <div v-show="!shouldCreateTag"> | ||||
|               <svg-icon name="octicon-git-branch"/> | ||||
|               <!-- eslint-disable-next-line vue/no-v-html --> | ||||
|               <span v-html="textCreateBranch.replace('%s', searchTerm)"/> | ||||
|             </div> | ||||
|             <div class="text small"> | ||||
|               <span v-if="isViewBranch || release">{{ textCreateBranchFrom.replace('%s', branchName) }}</span> | ||||
|               <span v-else-if="isViewTag">{{ textCreateBranchFrom.replace('%s', tagName) }}</span> | ||||
|               <span v-else>{{ textCreateBranchFrom.replace('%s', commitIdShort) }}</span> | ||||
|             </div> | ||||
|           </a> | ||||
|           <form ref="newBranchForm" :action="formActionUrl" method="post"> | ||||
|             <input type="hidden" name="_csrf" :value="csrfToken"> | ||||
|             <input type="hidden" name="new_branch_name" v-model="searchTerm"> | ||||
|             <input type="hidden" name="create_tag" v-model="shouldCreateTag"> | ||||
|             <input type="hidden" name="current_path" v-model="treePath" v-if="treePath"> | ||||
|           </form> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="message" v-if="showNoResults && !isLoading"> | ||||
|         {{ noResults }} | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <style scoped> | ||||
| .branch-tag-tab { | ||||
|   padding: 0 10px; | ||||
|   | ||||
| @@ -1,28 +1,3 @@ | ||||
| <template> | ||||
|   <div v-for="category in categories" :key="category" class="field gt-pl-2 gt-pb-2 access-token-category"> | ||||
|     <label class="category-label" :for="'access-token-scope-' + category"> | ||||
|       {{ category }} | ||||
|     </label> | ||||
|     <div class="gitea-select"> | ||||
|       <select | ||||
|         class="ui selection access-token-select" | ||||
|         name="scope" | ||||
|         :id="'access-token-scope-' + category" | ||||
|       > | ||||
|         <option value=""> | ||||
|           {{ noAccessLabel }} | ||||
|         </option> | ||||
|         <option :value="'read:' + category"> | ||||
|           {{ readLabel }} | ||||
|         </option> | ||||
|         <option :value="'write:' + category"> | ||||
|           {{ writeLabel }} | ||||
|         </option> | ||||
|       </select> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {createApp} from 'vue'; | ||||
| import {hideElem, showElem} from '../utils/dom.js'; | ||||
| @@ -111,3 +86,27 @@ export function initScopedAccessTokenCategories() { | ||||
| } | ||||
|  | ||||
| </script> | ||||
| <template> | ||||
|   <div v-for="category in categories" :key="category" class="field gt-pl-2 gt-pb-2 access-token-category"> | ||||
|     <label class="category-label" :for="'access-token-scope-' + category"> | ||||
|       {{ category }} | ||||
|     </label> | ||||
|     <div class="gitea-select"> | ||||
|       <select | ||||
|         class="ui selection access-token-select" | ||||
|         name="scope" | ||||
|         :id="'access-token-scope-' + category" | ||||
|       > | ||||
|         <option value=""> | ||||
|           {{ noAccessLabel }} | ||||
|         </option> | ||||
|         <option :value="'read:' + category"> | ||||
|           {{ readLabel }} | ||||
|         </option> | ||||
|         <option :value="'write:' + category"> | ||||
|           {{ writeLabel }} | ||||
|         </option> | ||||
|       </select> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user