Fix incorrect checkbox behaviors in the dashboard repolist's filter (#23147)
Co-author: yp05327 , this PR is based on yp05327's #22813. The problems of the old DashboardRepoList / repolist.tmpl: * It mixes many different frameworks together * It "just works", bug on bug * It uses many anti-pattern of Vue This PR: * Fix bugs and close #22800 * Decouple the "checkbox" elements from Fomantic UI (only use CSS styles) * Simplify the HTML layout * Simplify JS logic * Make it easier to refactor the DashboardRepoList into a pure Vue component in the future. ### Screenshots #### Default ![image](https://user-images.githubusercontent.com/2114189/221355768-a3eb5b23-85b4-4e3d-b906-844d8b15539d.png) #### Click "Archived" to make it checked ![image](https://user-images.githubusercontent.com/2114189/221355777-9a104ddf-52a7-4504-869a-43a73827d802.png) #### Click "Archived" to make it intermediate ![image](https://user-images.githubusercontent.com/2114189/221355802-0f67a073-67ad-4e92-84a6-558c432103a5.png) #### Click "Archived" to make it unchecked ![image](https://user-images.githubusercontent.com/2114189/221355810-acf1d9d8-ccce-47fe-a02e-70cf4e666331.png) --------- Co-authored-by: yp05327 <576951401@qq.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
3e426bba78
commit
7a5af25592
|
@ -46,49 +46,32 @@
|
||||||
<div class="ui dropdown icon button" title="{{.locale.Tr "home.filter"}}">
|
<div class="ui dropdown icon button" title="{{.locale.Tr "home.filter"}}">
|
||||||
<i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i>
|
<i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="item">
|
<a class="item" @click="toggleArchivedFilter()">
|
||||||
<a @click="toggleArchivedFilter()">
|
<div class="ui checkbox"
|
||||||
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_both_archived_unarchived"}}" v-if="archivedFilter === 'both'">
|
ref="checkboxArchivedFilter"
|
||||||
<input type="checkbox">
|
data-title-both="{{.locale.Tr "home.show_both_archived_unarchived"}}"
|
||||||
<label>
|
data-title-unarchived="{{.locale.Tr "home.show_only_unarchived"}}"
|
||||||
{{svg "octicon-archive" 16 "gt-mr-2"}}
|
data-title-archived="{{.locale.Tr "home.show_only_archived"}}"
|
||||||
{{.locale.Tr "home.show_archived"}}
|
:title="checkboxArchivedFilterTitle"
|
||||||
</label>
|
>
|
||||||
</div>
|
<!--the "hidden" is necessary to make the checkbox work without Fomantic UI js,
|
||||||
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_only_unarchived"}}" v-if="archivedFilter === 'unarchived'">
|
otherwise if the "input" handles click event for intermediate status, it breaks the internal state-->
|
||||||
<input type="checkbox">
|
<input type="checkbox" class="hidden" v-bind.prop="checkboxArchivedFilterProps">
|
||||||
<label>
|
|
||||||
{{svg "octicon-archive" 16 "gt-mr-2"}}
|
|
||||||
{{.locale.Tr "home.show_archived"}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_only_archived"}}" v-if="archivedFilter === 'archived'">
|
|
||||||
<input type="checkbox">
|
|
||||||
<label>
|
<label>
|
||||||
{{svg "octicon-archive" 16 "gt-mr-2"}}
|
{{svg "octicon-archive" 16 "gt-mr-2"}}
|
||||||
{{.locale.Tr "home.show_archived"}}
|
{{.locale.Tr "home.show_archived"}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
<a class="item" @click="togglePrivateFilter()">
|
||||||
<div class="item">
|
<div class="ui checkbox"
|
||||||
<a @click="togglePrivateFilter()">
|
ref="checkboxPrivateFilter"
|
||||||
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_both_private_public"}}" v-if="privateFilter === 'both'">
|
data-title-both="{{.locale.Tr "home.show_both_private_public"}}"
|
||||||
<input type="checkbox">
|
data-title-public="{{.locale.Tr "home.show_only_public"}}"
|
||||||
<label>
|
data-title-private="{{.locale.Tr "home.show_only_private"}}"
|
||||||
{{svg "octicon-lock" 16 "gt-mr-2"}}
|
:title="checkboxPrivateFilterTitle"
|
||||||
{{.locale.Tr "home.show_private"}}
|
>
|
||||||
</label>
|
<input type="checkbox" class="hidden" v-bind.prop="checkboxPrivateFilterProps">
|
||||||
</div>
|
|
||||||
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_only_public"}}" v-if="privateFilter === 'public'">
|
|
||||||
<input type="checkbox">
|
|
||||||
<label>
|
|
||||||
{{svg "octicon-lock" 16 "gt-mr-2"}}
|
|
||||||
{{.locale.Tr "home.show_private"}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_only_private"}}" v-if="privateFilter === 'private'">
|
|
||||||
<input type="checkbox">
|
|
||||||
<label>
|
<label>
|
||||||
{{svg "octicon-lock" 16 "gt-mr-2"}}
|
{{svg "octicon-lock" 16 "gt-mr-2"}}
|
||||||
{{.locale.Tr "home.show_private"}}
|
{{.locale.Tr "home.show_private"}}
|
||||||
|
@ -98,7 +81,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="ui secondary tiny pointing borderless menu center grid repos-filter">
|
<div class="ui secondary tiny pointing borderless menu center grid repos-filter">
|
||||||
<a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
|
<a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
|
||||||
{{.locale.Tr "all"}}
|
{{.locale.Tr "all"}}
|
||||||
|
|
|
@ -87,6 +87,7 @@ function initVueComponents(app) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
hasMounted: false, // accessing $refs in computed() need to wait for mounted
|
||||||
tab,
|
tab,
|
||||||
repos: [],
|
repos: [],
|
||||||
reposTotalCount: 0,
|
reposTotalCount: 0,
|
||||||
|
@ -134,7 +135,19 @@ function initVueComponents(app) {
|
||||||
},
|
},
|
||||||
repoTypeCount() {
|
repoTypeCount() {
|
||||||
return this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`];
|
return this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`];
|
||||||
}
|
},
|
||||||
|
checkboxArchivedFilterTitle() {
|
||||||
|
return this.hasMounted && this.$refs.checkboxArchivedFilter?.getAttribute(`data-title-${this.archivedFilter}`);
|
||||||
|
},
|
||||||
|
checkboxArchivedFilterProps() {
|
||||||
|
return {checked: this.archivedFilter === 'archived', indeterminate: this.archivedFilter === 'both'};
|
||||||
|
},
|
||||||
|
checkboxPrivateFilterTitle() {
|
||||||
|
return this.hasMounted && this.$refs.checkboxPrivateFilter?.getAttribute(`data-title-${this.privateFilter}`);
|
||||||
|
},
|
||||||
|
checkboxPrivateFilterProps() {
|
||||||
|
return {checked: this.privateFilter === 'private', indeterminate: this.privateFilter === 'both'};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -144,10 +157,11 @@ function initVueComponents(app) {
|
||||||
initTooltip(elTooltip);
|
initTooltip(elTooltip);
|
||||||
}
|
}
|
||||||
$(el).find('.dropdown').dropdown();
|
$(el).find('.dropdown').dropdown();
|
||||||
this.setCheckboxes();
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
this.$refs.search.focus();
|
this.$refs.search.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.hasMounted = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -156,39 +170,6 @@ function initVueComponents(app) {
|
||||||
this.updateHistory();
|
this.updateHistory();
|
||||||
},
|
},
|
||||||
|
|
||||||
setCheckboxes() {
|
|
||||||
switch (this.archivedFilter) {
|
|
||||||
case 'unarchived':
|
|
||||||
$('#archivedFilterCheckbox').checkbox('set unchecked');
|
|
||||||
break;
|
|
||||||
case 'archived':
|
|
||||||
$('#archivedFilterCheckbox').checkbox('set checked');
|
|
||||||
break;
|
|
||||||
case 'both':
|
|
||||||
$('#archivedFilterCheckbox').checkbox('set indeterminate');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.archivedFilter = 'unarchived';
|
|
||||||
$('#archivedFilterCheckbox').checkbox('set unchecked');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (this.privateFilter) {
|
|
||||||
case 'public':
|
|
||||||
$('#privateFilterCheckbox').checkbox('set unchecked');
|
|
||||||
break;
|
|
||||||
case 'private':
|
|
||||||
$('#privateFilterCheckbox').checkbox('set checked');
|
|
||||||
break;
|
|
||||||
case 'both':
|
|
||||||
$('#privateFilterCheckbox').checkbox('set indeterminate');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.privateFilter = 'both';
|
|
||||||
$('#privateFilterCheckbox').checkbox('set indeterminate');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
changeReposFilter(filter) {
|
changeReposFilter(filter) {
|
||||||
this.reposFilter = filter;
|
this.reposFilter = filter;
|
||||||
this.repos = [];
|
this.repos = [];
|
||||||
|
@ -245,45 +226,29 @@ function initVueComponents(app) {
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleArchivedFilter() {
|
toggleArchivedFilter() {
|
||||||
switch (this.archivedFilter) {
|
if (this.archivedFilter === 'unarchived') {
|
||||||
case 'both':
|
|
||||||
this.archivedFilter = 'unarchived';
|
|
||||||
break;
|
|
||||||
case 'unarchived':
|
|
||||||
this.archivedFilter = 'archived';
|
this.archivedFilter = 'archived';
|
||||||
break;
|
} else if (this.archivedFilter === 'archived') {
|
||||||
case 'archived':
|
|
||||||
this.archivedFilter = 'both';
|
this.archivedFilter = 'both';
|
||||||
break;
|
} else { // including both
|
||||||
default:
|
|
||||||
this.archivedFilter = 'unarchived';
|
this.archivedFilter = 'unarchived';
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.repos = [];
|
this.repos = [];
|
||||||
this.setCheckboxes();
|
|
||||||
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
|
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
|
||||||
this.searchRepos();
|
this.searchRepos();
|
||||||
},
|
},
|
||||||
|
|
||||||
togglePrivateFilter() {
|
togglePrivateFilter() {
|
||||||
switch (this.privateFilter) {
|
if (this.privateFilter === 'both') {
|
||||||
case 'both':
|
|
||||||
this.privateFilter = 'public';
|
this.privateFilter = 'public';
|
||||||
break;
|
} else if (this.privateFilter === 'public') {
|
||||||
case 'public':
|
|
||||||
this.privateFilter = 'private';
|
this.privateFilter = 'private';
|
||||||
break;
|
} else { // including private
|
||||||
case 'private':
|
|
||||||
this.privateFilter = 'both';
|
this.privateFilter = 'both';
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.privateFilter = 'both';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.repos = [];
|
this.repos = [];
|
||||||
this.setCheckboxes();
|
|
||||||
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
|
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
|
||||||
this.searchRepos();
|
this.searchRepos();
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue