aboutsummaryrefslogtreecommitdiffstats
path: root/web_src
diff options
context:
space:
mode:
authorAndré Jaenisch <Ryuno-Ki@users.noreply.github.com>2022-10-01 16:26:38 +0200
committerGitHub <noreply@github.com>2022-10-01 22:26:38 +0800
commit04e97b83115e7439d43c0ede5fe2d1b50d201c52 (patch)
tree073a20e9377c5197112327afd11d7a6f2cccb268 /web_src
parent726afe8a9e33128476e1dc85f262fe56f995d12c (diff)
downloadgitea-04e97b83115e7439d43c0ede5fe2d1b50d201c52.tar.gz
gitea-04e97b83115e7439d43c0ede5fe2d1b50d201c52.zip
Refactor from Vue2 to Vue3 (#20044)
Close #19902
Diffstat (limited to 'web_src')
-rw-r--r--web_src/js/components/ActivityHeatmap.vue2
-rw-r--r--web_src/js/components/ContextPopup.vue12
-rw-r--r--web_src/js/components/DashboardRepoList.js40
-rw-r--r--web_src/js/components/DiffFileList.vue4
-rw-r--r--web_src/js/components/DiffFileTree.vue8
-rw-r--r--web_src/js/components/RepoBranchTagDropdown.js43
-rw-r--r--web_src/js/components/VueComponentLoader.js20
-rw-r--r--web_src/js/features/contextpopup.js13
-rw-r--r--web_src/js/features/heatmap.js8
-rw-r--r--web_src/js/features/repo-diff-filetree.js18
-rw-r--r--web_src/js/features/repo-issue-pr-form.js8
-rw-r--r--web_src/js/svg.js7
-rw-r--r--web_src/less/features/heatmap.less8
13 files changed, 95 insertions, 96 deletions
diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue
index fa2c43b5b4..b5c5c65bbf 100644
--- a/web_src/js/components/ActivityHeatmap.vue
+++ b/web_src/js/components/ActivityHeatmap.vue
@@ -15,7 +15,7 @@
</div>
</template>
<script>
-import {CalendarHeatmap} from 'vue-calendar-heatmap';
+import {CalendarHeatmap} from 'vue3-calendar-heatmap';
export default {
name: 'ActivityHeatmap',
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue
index c176a18659..0b086690a9 100644
--- a/web_src/js/components/ContextPopup.vue
+++ b/web_src/js/components/ContextPopup.vue
@@ -1,5 +1,5 @@
<template>
- <div>
+ <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>
@@ -109,15 +109,16 @@ export default {
},
mounted() {
- this.$root.$on('load-context-popup', (data, callback) => {
+ this.$refs.root.addEventListener('us-load-context-popup', (e) => {
+ const data = e.detail;
if (!this.loading && this.issue === null) {
- this.load(data, callback);
+ this.load(data);
}
});
},
methods: {
- load(data, callback) {
+ load(data) {
this.loading = true;
this.i18nErrorMessage = null;
$.get(`${appSubUrl}/${data.owner}/${data.repo}/issues/${data.index}/info`).done((issue) => {
@@ -130,9 +131,6 @@ export default {
}
}).always(() => {
this.loading = false;
- if (callback) {
- this.$nextTick(callback);
- }
});
}
}
diff --git a/web_src/js/components/DashboardRepoList.js b/web_src/js/components/DashboardRepoList.js
index cbbc12c2c4..0a009e78d1 100644
--- a/web_src/js/components/DashboardRepoList.js
+++ b/web_src/js/components/DashboardRepoList.js
@@ -1,12 +1,12 @@
-import Vue from 'vue';
+import {createApp, nextTick} from 'vue';
import $ from 'jquery';
import {initVueSvg, vueDelimiters} from './VueComponentLoader.js';
import {initTooltip} from '../modules/tippy.js';
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
-function initVueComponents() {
- Vue.component('repo-search', {
+function initVueComponents(app) {
+ app.component('repo-search', {
delimiters: vueDelimiters,
props: {
searchLimit: {
@@ -138,13 +138,14 @@ function initVueComponents() {
},
mounted() {
+ const el = document.getElementById('dashboard-repo-list');
this.changeReposFilter(this.reposFilter);
- for (const el of this.$el.querySelectorAll('.tooltip')) {
- initTooltip(el);
+ for (const elTooltip of el.querySelectorAll('.tooltip')) {
+ initTooltip(elTooltip);
}
- $(this.$el).find('.dropdown').dropdown();
+ $(el).find('.dropdown').dropdown();
this.setCheckboxes();
- Vue.nextTick(() => {
+ nextTick(() => {
this.$refs.search.focus();
});
},
@@ -192,7 +193,7 @@ function initVueComponents() {
this.reposFilter = filter;
this.repos = [];
this.page = 1;
- Vue.set(this.counts, `${filter}:${this.archivedFilter}:${this.privateFilter}`, 0);
+ this.counts[`${filter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos();
},
@@ -261,7 +262,7 @@ function initVueComponents() {
this.page = 1;
this.repos = [];
this.setCheckboxes();
- Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0);
+ this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos();
},
@@ -283,7 +284,7 @@ function initVueComponents() {
this.page = 1;
this.repos = [];
this.setCheckboxes();
- Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0);
+ this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos();
},
@@ -297,7 +298,7 @@ function initVueComponents() {
this.page = 1;
}
this.repos = [];
- Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0);
+ this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos();
},
@@ -331,7 +332,7 @@ function initVueComponents() {
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
this.reposTotalCount = count;
}
- Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count);
+ this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = count;
this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory();
this.isLoading = false;
@@ -352,22 +353,20 @@ function initVueComponents() {
}
return 'octicon-repo';
}
- }
+ },
+
+ template: document.getElementById('dashboard-repo-list-template'),
});
}
-
export function initDashboardRepoList() {
const el = document.getElementById('dashboard-repo-list');
const dashboardRepoListData = pageData.dashboardRepoList || null;
if (!el || !dashboardRepoListData) return;
- initVueSvg();
- initVueComponents();
- new Vue({
- el,
+ const app = createApp({
delimiters: vueDelimiters,
- data: () => {
+ data() {
return {
searchLimit: dashboardRepoListData.searchLimit || 0,
subUrl: appSubUrl,
@@ -375,4 +374,7 @@ export function initDashboardRepoList() {
};
},
});
+ initVueSvg(app);
+ initVueComponents(app);
+ app.mount(el);
}
diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue
index 2a9aa77377..da53be3b46 100644
--- a/web_src/js/components/DiffFileList.vue
+++ b/web_src/js/components/DiffFileList.vue
@@ -1,5 +1,5 @@
<template>
- <ol class="diff-detail-box diff-stats m-0" id="diff-files" v-if="fileListIsVisible">
+ <ol class="diff-detail-box diff-stats m-0" ref="root" v-if="fileListIsVisible">
<li v-for="file in files" :key="file.NameHash">
<div class="bold df ac pull-right">
<span v-if="file.IsBin" class="ml-1 mr-3">{{ binaryFileMessage }}</span>
@@ -37,7 +37,7 @@ export default {
fileListIsVisible(newValue) {
if (newValue === true) {
this.$nextTick(() => {
- for (const el of this.$el.querySelectorAll('.tooltip')) {
+ for (const el of this.$refs.root.querySelectorAll('.tooltip')) {
initTooltip(el);
}
});
diff --git a/web_src/js/components/DiffFileTree.vue b/web_src/js/components/DiffFileTree.vue
index d0962254cd..717ba70f7f 100644
--- a/web_src/js/components/DiffFileTree.vue
+++ b/web_src/js/components/DiffFileTree.vue
@@ -1,11 +1,10 @@
<template>
<div
- v-show="fileTreeIsVisible"
- id="diff-file-tree"
+ v-if="fileTreeIsVisible"
class="mr-3 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 -->
- <div class="ui list" v-if="fileTreeIsVisible">
+ <div class="ui list">
<DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item" />
</div>
<div v-if="isIncomplete" id="diff-too-many-files-stats" class="pt-2">
@@ -117,6 +116,9 @@ export default {
const [toShow, toHide] = document.querySelectorAll('.diff-toggle-file-tree-button .icon');
toShow.classList.toggle('hide', visible); // hide the toShow icon if the tree is visible
toHide.classList.toggle('hide', !visible); // similarly
+
+ const diffTree = document.getElementById('diff-file-tree');
+ diffTree.classList.toggle('hide', !visible);
},
loadMoreData() {
this.isLoadingNewData = true;
diff --git a/web_src/js/components/RepoBranchTagDropdown.js b/web_src/js/components/RepoBranchTagDropdown.js
index d55fa91b92..8bed305fca 100644
--- a/web_src/js/components/RepoBranchTagDropdown.js
+++ b/web_src/js/components/RepoBranchTagDropdown.js
@@ -1,4 +1,4 @@
-import Vue from 'vue';
+import {createApp, nextTick} from 'vue';
import $ from 'jquery';
import {vueDelimiters} from './VueComponentLoader.js';
@@ -37,10 +37,14 @@ export function initRepoBranchTagDropdown(selector) {
});
});
$data.remove();
- new Vue({
- el: this,
+
+ // eslint-disable-next-line unicorn/no-this-assignment
+ const elRoot = this;
+ const view = createApp({
delimiters: vueDelimiters,
- data,
+ data() {
+ return data;
+ },
computed: {
filteredItems() {
const items = this.items.filter((item) => {
@@ -73,10 +77,10 @@ export function initRepoBranchTagDropdown(selector) {
},
beforeMount() {
- this.noResults = this.$el.getAttribute('data-no-results');
- this.canCreateBranch = this.$el.getAttribute('data-can-create-branch') === 'true';
- this.branchForm = this.$el.getAttribute('data-branch-form');
- switch (this.$el.getAttribute('data-view-type')) {
+ this.noResults = elRoot.getAttribute('data-no-results');
+ this.canCreateBranch = elRoot.getAttribute('data-can-create-branch') === 'true';
+ this.branchForm = elRoot.getAttribute('data-branch-form');
+ switch (elRoot.getAttribute('data-view-type')) {
case 'tree':
this.isViewTree = true;
break;
@@ -87,19 +91,19 @@ export function initRepoBranchTagDropdown(selector) {
this.isViewBranch = true;
break;
}
- this.refName = this.$el.getAttribute('data-ref-name');
- this.branchURLPrefix = this.$el.getAttribute('data-branch-url-prefix');
- this.branchURLSuffix = this.$el.getAttribute('data-branch-url-suffix');
- this.tagURLPrefix = this.$el.getAttribute('data-tag-url-prefix');
- this.tagURLSuffix = this.$el.getAttribute('data-tag-url-suffix');
- this.setAction = this.$el.getAttribute('data-set-action') === 'true';
- this.submitForm = this.$el.getAttribute('data-submit-form') === 'true';
+ this.refName = elRoot.getAttribute('data-ref-name');
+ this.branchURLPrefix = elRoot.getAttribute('data-branch-url-prefix');
+ this.branchURLSuffix = elRoot.getAttribute('data-branch-url-suffix');
+ this.tagURLPrefix = elRoot.getAttribute('data-tag-url-prefix');
+ this.tagURLSuffix = elRoot.getAttribute('data-tag-url-suffix');
+ this.setAction = elRoot.getAttribute('data-set-action') === 'true';
+ this.submitForm = elRoot.getAttribute('data-submit-form') === 'true';
document.body.addEventListener('click', (event) => {
- if (this.$el.contains(event.target)) return;
+ if (elRoot.contains(event.target)) return;
if (this.menuVisible) {
- Vue.set(this, 'menuVisible', false);
+ this.menuVisible = false;
}
});
},
@@ -135,7 +139,7 @@ export function initRepoBranchTagDropdown(selector) {
if (this.submitForm) {
$(`#${this.branchForm}`).trigger('submit');
}
- Vue.set(this, 'menuVisible', false);
+ this.menuVisible = false;
}
},
createNewBranch() {
@@ -143,7 +147,7 @@ export function initRepoBranchTagDropdown(selector) {
$(this.$refs.newBranchForm).trigger('submit');
},
focusSearchField() {
- Vue.nextTick(() => {
+ nextTick(() => {
this.$refs.searchField.focus();
});
},
@@ -213,5 +217,6 @@ export function initRepoBranchTagDropdown(selector) {
}
}
});
+ view.mount(this);
});
}
diff --git a/web_src/js/components/VueComponentLoader.js b/web_src/js/components/VueComponentLoader.js
index 2979cd6a86..f0555b21cc 100644
--- a/web_src/js/components/VueComponentLoader.js
+++ b/web_src/js/components/VueComponentLoader.js
@@ -1,4 +1,4 @@
-import Vue from 'vue';
+import {createApp} from 'vue';
import {svgs} from '../svg.js';
export const vueDelimiters = ['${', '}'];
@@ -8,13 +8,14 @@ export function initVueEnv() {
if (vueEnvInited) return;
vueEnvInited = true;
- const isProd = window.config.runModeIsProd;
- Vue.config.productionTip = false;
- Vue.config.devtools = !isProd;
+ // As far as I could tell, this is no longer possible.
+ // But there seem not to be a guide what to do instead.
+ // const isProd = window.config.runModeIsProd;
+ // Vue.config.devtools = !isProd;
}
let vueSvgInited = false;
-export function initVueSvg() {
+export function initVueSvg(app) {
if (vueSvgInited) return;
vueSvgInited = true;
@@ -24,7 +25,7 @@ export function initVueSvg() {
.replace(/height="[0-9]+"/, 'v-bind:height="size"')
.replace(/width="[0-9]+"/, 'v-bind:width="size"');
- Vue.component(name, {
+ app.component(name, {
props: {
size: {
type: String,
@@ -42,8 +43,7 @@ export function initVueApp(el, opts = {}) {
}
if (!el) return null;
- return new Vue(Object.assign({
- el,
- delimiters: vueDelimiters,
- }, opts));
+ return createApp(
+ Object.assign({delimiters: vueDelimiters}, opts)
+ ).mount(el);
}
diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js
index f4e660be3f..d29da6d951 100644
--- a/web_src/js/features/contextpopup.js
+++ b/web_src/js/features/contextpopup.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import Vue from 'vue';
+import {createApp} from 'vue';
import ContextPopup from '../components/ContextPopup.vue';
import {parseIssueHref} from '../utils.js';
import {createTippy} from '../modules/tippy.js';
@@ -17,17 +17,12 @@ export default function initContextPopups() {
if (!owner) return;
const el = document.createElement('div');
- el.innerHTML = '<div></div>';
this.parentNode.insertBefore(el, this.nextSibling);
- const View = Vue.extend({
- render: (createElement) => createElement(ContextPopup),
- });
-
- const view = new View();
+ const view = createApp(ContextPopup);
try {
- view.$mount(el.firstChild);
+ view.mount(el);
} catch (err) {
console.error(err);
el.textContent = 'ContextPopup failed to load';
@@ -37,7 +32,7 @@ export default function initContextPopups() {
content: el,
interactive: true,
onShow: () => {
- view.$emit('load-context-popup', {owner, repo, index});
+ el.firstChild.dispatchEvent(new CustomEvent('us-load-context-popup', {detail: {owner, repo, index}}));
}
});
});
diff --git a/web_src/js/features/heatmap.js b/web_src/js/features/heatmap.js
index 363be5ca75..6e6202e866 100644
--- a/web_src/js/features/heatmap.js
+++ b/web_src/js/features/heatmap.js
@@ -1,4 +1,4 @@
-import Vue from 'vue';
+import {createApp} from 'vue';
import ActivityHeatmap from '../components/ActivityHeatmap.vue';
export default function initHeatmap() {
@@ -17,11 +17,9 @@ export default function initHeatmap() {
return {date: new Date(v), count: heatmap[v]};
});
- const View = Vue.extend({
- render: (createElement) => createElement(ActivityHeatmap, {props: {values}}),
- });
+ const View = createApp(ActivityHeatmap, {values});
- new View().$mount(el);
+ View.mount(el);
} catch (err) {
console.error('Heatmap failed to load', err);
el.textContent = 'Heatmap failed to load';
diff --git a/web_src/js/features/repo-diff-filetree.js b/web_src/js/features/repo-diff-filetree.js
index 9eba3cf887..6059dd82e7 100644
--- a/web_src/js/features/repo-diff-filetree.js
+++ b/web_src/js/features/repo-diff-filetree.js
@@ -1,21 +1,17 @@
-import Vue from 'vue';
+import {createApp} from 'vue';
import DiffFileTree from '../components/DiffFileTree.vue';
import DiffFileList from '../components/DiffFileList.vue';
export default function initDiffFileTree() {
- const el = document.getElementById('diff-file-tree-container');
+ const el = document.getElementById('diff-file-tree');
if (!el) return;
- const View = Vue.extend({
- render: (createElement) => createElement(DiffFileTree),
- });
- new View().$mount(el);
+ const fileTreeView = createApp(DiffFileTree);
+ fileTreeView.mount(el);
- const fileListElement = document.getElementById('diff-file-list-container');
+ const fileListElement = document.getElementById('diff-file-list');
if (!fileListElement) return;
- const fileListView = Vue.extend({
- render: (createElement) => createElement(DiffFileList),
- });
- new fileListView().$mount(fileListElement);
+ const fileListView = createApp(DiffFileList);
+ fileListView.mount(fileListElement);
}
diff --git a/web_src/js/features/repo-issue-pr-form.js b/web_src/js/features/repo-issue-pr-form.js
index 747e4f467e..59d4c7a3b4 100644
--- a/web_src/js/features/repo-issue-pr-form.js
+++ b/web_src/js/features/repo-issue-pr-form.js
@@ -1,12 +1,10 @@
-import Vue from 'vue';
+import {createApp} from 'vue';
import PullRequestMergeForm from '../components/PullRequestMergeForm.vue';
export default function initPullRequestMergeForm() {
const el = document.getElementById('pull-request-merge-form');
if (!el) return;
- const View = Vue.extend({
- render: (createElement) => createElement(PullRequestMergeForm),
- });
- new View().$mount(el);
+ const view = createApp(PullRequestMergeForm);
+ view.mount(el);
}
diff --git a/web_src/js/svg.js b/web_src/js/svg.js
index 6677bf83cb..dedc126303 100644
--- a/web_src/js/svg.js
+++ b/web_src/js/svg.js
@@ -26,8 +26,6 @@ import octiconSidebarExpand from '../../public/img/svg/octicon-sidebar-expand.sv
import octiconSidebarCollapse from '../../public/img/svg/octicon-sidebar-collapse.svg';
-import Vue from 'vue';
-
export const svgs = {
'octicon-chevron-down': octiconChevronDown,
'octicon-chevron-right': octiconChevronRight,
@@ -74,7 +72,8 @@ export function svg(name, size = 16, className = '') {
return serializer.serializeToString(svgNode);
}
-export const SvgIcon = Vue.component('SvgIcon', {
+export const SvgIcon = {
+ name: 'SvgIcon',
props: {
name: {type: String, required: true},
size: {type: Number, default: 16},
@@ -88,4 +87,4 @@ export const SvgIcon = Vue.component('SvgIcon', {
},
template: `<span v-html="svg" />`
-});
+};
diff --git a/web_src/less/features/heatmap.less b/web_src/less/features/heatmap.less
index f551daf333..952d6f35ed 100644
--- a/web_src/less/features/heatmap.less
+++ b/web_src/less/features/heatmap.less
@@ -3,10 +3,16 @@
text-align: center;
position: relative;
min-height: 125px;
- display: flex;
align-items: center;
justify-content: center;
+ // for the "Less" and "More" legend
+ .vch__legend .vch__legend div:first-child,
+ .vch__legend .vch__legend div:last-child {
+ display: inline-block;
+ padding: 0 5px;
+ }
+
> svg {
width: 100%;
}