diff options
author | silverwind <me@silverwind.io> | 2023-09-02 16:59:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-02 14:59:07 +0000 |
commit | 9a3de436f452144933888fb1e4f336efeb93293a (patch) | |
tree | e49c4037604de030a8552a5deca180c240c28484 /web_src/js | |
parent | 79f73299710fe8596b6ea31c0fa0ab97ab3dc801 (diff) | |
download | gitea-9a3de436f452144933888fb1e4f336efeb93293a.tar.gz gitea-9a3de436f452144933888fb1e4f336efeb93293a.zip |
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>
Diffstat (limited to 'web_src/js')
-rw-r--r-- | web_src/js/components/ActionRunStatus.vue | 21 | ||||
-rw-r--r-- | web_src/js/components/ActivityHeatmap.vue | 28 | ||||
-rw-r--r-- | web_src/js/components/ContextPopup.vue | 49 | ||||
-rw-r--r-- | web_src/js/components/DashboardRepoList.vue | 298 | ||||
-rw-r--r-- | web_src/js/components/DiffCommitSelector.vue | 143 | ||||
-rw-r--r-- | web_src/js/components/DiffFileList.vue | 43 | ||||
-rw-r--r-- | web_src/js/components/DiffFileTree.vue | 19 | ||||
-rw-r--r-- | web_src/js/components/DiffFileTreeItem.vue | 46 | ||||
-rw-r--r-- | web_src/js/components/PullRequestMergeForm.vue | 156 | ||||
-rw-r--r-- | web_src/js/components/RepoActionView.vue | 241 | ||||
-rw-r--r-- | web_src/js/components/RepoActivityTopAuthors.vue | 101 | ||||
-rw-r--r-- | web_src/js/components/RepoBranchTagSelector.vue | 146 | ||||
-rw-r--r-- | web_src/js/components/ScopedAccessTokenSelector.vue | 49 |
13 files changed, 661 insertions, 679 deletions
diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue index 1955decc1e..51a7745431 100644 --- a/web_src/js/components/ActionRunStatus.vue +++ b/web_src/js/components/ActionRunStatus.vue @@ -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> diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue index 1b083ed134..96a6e68012 100644 --- a/web_src/js/components/ActivityHeatmap.vue +++ b/web_src/js/components/ActivityHeatmap.vue @@ -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> diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index b6852655f6..303e6d0c89 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -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> diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index 898362776b..5b8075f07a 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -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; diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue index 283ef03ab9..48dc9d72ff 100644 --- a/web_src/js/components/DiffCommitSelector.vue +++ b/web_src/js/components/DiffCommitSelector.vue @@ -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; diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue index 131be01811..103526d025 100644 --- a/web_src/js/components/DiffFileList.vue +++ b/web_src/js/components/DiffFileList.vue @@ -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> diff --git a/web_src/js/components/DiffFileTree.vue b/web_src/js/components/DiffFileTree.vue index e02222bde0..8ee86857ac 100644 --- a/web_src/js/components/DiffFileTree.vue +++ b/web_src/js/components/DiffFileTree.vue @@ -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> diff --git a/web_src/js/components/DiffFileTreeItem.vue b/web_src/js/components/DiffFileTreeItem.vue index d21afc42cb..553ab1464f 100644 --- a/web_src/js/components/DiffFileTreeItem.vue +++ b/web_src/js/components/DiffFileTreeItem.vue @@ -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; diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue index 673773a91b..cca8b9dd6e 100644 --- a/web_src/js/components/PullRequestMergeForm.vue +++ b/web_src/js/components/PullRequestMergeForm.vue @@ -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 { diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 8d18ad2301..925ff7e087 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -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; diff --git a/web_src/js/components/RepoActivityTopAuthors.vue b/web_src/js/components/RepoActivityTopAuthors.vue index 294ee6f7bc..fe41218d88 100644 --- a/web_src/js/components/RepoActivityTopAuthors.vue +++ b/web_src/js/components/RepoActivityTopAuthors.vue @@ -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> diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index 1aba3b2935..b64b66d181 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -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; diff --git a/web_src/js/components/ScopedAccessTokenSelector.vue b/web_src/js/components/ScopedAccessTokenSelector.vue index b4b9b979ea..f6af7e447f 100644 --- a/web_src/js/components/ScopedAccessTokenSelector.vue +++ b/web_src/js/components/ScopedAccessTokenSelector.vue @@ -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> |