Skip to content

Commit 7a5af25

Browse files
wxiaoguangyp05327lunny
authored
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 <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent 3e426bb commit 7a5af25

File tree

2 files changed

+60
-113
lines changed

2 files changed

+60
-113
lines changed

templates/user/dashboard/repolist.tmpl

+32-50
Original file line numberDiff line numberDiff line change
@@ -46,56 +46,38 @@
4646
<div class="ui dropdown icon button" title="{{.locale.Tr "home.filter"}}">
4747
<i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i>
4848
<div class="menu">
49-
<div class="item">
50-
<a @click="toggleArchivedFilter()">
51-
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_both_archived_unarchived"}}" v-if="archivedFilter === 'both'">
52-
<input type="checkbox">
53-
<label>
54-
{{svg "octicon-archive" 16 "gt-mr-2"}}
55-
{{.locale.Tr "home.show_archived"}}
56-
</label>
57-
</div>
58-
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_only_unarchived"}}" v-if="archivedFilter === 'unarchived'">
59-
<input type="checkbox">
60-
<label>
61-
{{svg "octicon-archive" 16 "gt-mr-2"}}
62-
{{.locale.Tr "home.show_archived"}}
63-
</label>
64-
</div>
65-
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_only_archived"}}" v-if="archivedFilter === 'archived'">
66-
<input type="checkbox">
67-
<label>
68-
{{svg "octicon-archive" 16 "gt-mr-2"}}
69-
{{.locale.Tr "home.show_archived"}}
70-
</label>
71-
</div>
72-
</a>
73-
</div>
74-
<div class="item">
75-
<a @click="togglePrivateFilter()">
76-
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_both_private_public"}}" v-if="privateFilter === 'both'">
77-
<input type="checkbox">
78-
<label>
79-
{{svg "octicon-lock" 16 "gt-mr-2"}}
80-
{{.locale.Tr "home.show_private"}}
81-
</label>
82-
</div>
83-
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_only_public"}}" v-if="privateFilter === 'public'">
84-
<input type="checkbox">
85-
<label>
86-
{{svg "octicon-lock" 16 "gt-mr-2"}}
87-
{{.locale.Tr "home.show_private"}}
88-
</label>
89-
</div>
90-
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_only_private"}}" v-if="privateFilter === 'private'">
91-
<input type="checkbox">
92-
<label>
93-
{{svg "octicon-lock" 16 "gt-mr-2"}}
94-
{{.locale.Tr "home.show_private"}}
95-
</label>
96-
</div>
97-
</a>
98-
</div>
49+
<a class="item" @click="toggleArchivedFilter()">
50+
<div class="ui checkbox"
51+
ref="checkboxArchivedFilter"
52+
data-title-both="{{.locale.Tr "home.show_both_archived_unarchived"}}"
53+
data-title-unarchived="{{.locale.Tr "home.show_only_unarchived"}}"
54+
data-title-archived="{{.locale.Tr "home.show_only_archived"}}"
55+
:title="checkboxArchivedFilterTitle"
56+
>
57+
<!--the "hidden" is necessary to make the checkbox work without Fomantic UI js,
58+
otherwise if the "input" handles click event for intermediate status, it breaks the internal state-->
59+
<input type="checkbox" class="hidden" v-bind.prop="checkboxArchivedFilterProps">
60+
<label>
61+
{{svg "octicon-archive" 16 "gt-mr-2"}}
62+
{{.locale.Tr "home.show_archived"}}
63+
</label>
64+
</div>
65+
</a>
66+
<a class="item" @click="togglePrivateFilter()">
67+
<div class="ui checkbox"
68+
ref="checkboxPrivateFilter"
69+
data-title-both="{{.locale.Tr "home.show_both_private_public"}}"
70+
data-title-public="{{.locale.Tr "home.show_only_public"}}"
71+
data-title-private="{{.locale.Tr "home.show_only_private"}}"
72+
:title="checkboxPrivateFilterTitle"
73+
>
74+
<input type="checkbox" class="hidden" v-bind.prop="checkboxPrivateFilterProps">
75+
<label>
76+
{{svg "octicon-lock" 16 "gt-mr-2"}}
77+
{{.locale.Tr "home.show_private"}}
78+
</label>
79+
</div>
80+
</a>
9981
</div>
10082
</div>
10183
</div>

web_src/js/components/DashboardRepoList.js

+28-63
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ function initVueComponents(app) {
8787
}
8888

8989
return {
90+
hasMounted: false, // accessing $refs in computed() need to wait for mounted
9091
tab,
9192
repos: [],
9293
reposTotalCount: 0,
@@ -134,7 +135,19 @@ function initVueComponents(app) {
134135
},
135136
repoTypeCount() {
136137
return this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`];
137-
}
138+
},
139+
checkboxArchivedFilterTitle() {
140+
return this.hasMounted && this.$refs.checkboxArchivedFilter?.getAttribute(`data-title-${this.archivedFilter}`);
141+
},
142+
checkboxArchivedFilterProps() {
143+
return {checked: this.archivedFilter === 'archived', indeterminate: this.archivedFilter === 'both'};
144+
},
145+
checkboxPrivateFilterTitle() {
146+
return this.hasMounted && this.$refs.checkboxPrivateFilter?.getAttribute(`data-title-${this.privateFilter}`);
147+
},
148+
checkboxPrivateFilterProps() {
149+
return {checked: this.privateFilter === 'private', indeterminate: this.privateFilter === 'both'};
150+
},
138151
},
139152

140153
mounted() {
@@ -144,10 +157,11 @@ function initVueComponents(app) {
144157
initTooltip(elTooltip);
145158
}
146159
$(el).find('.dropdown').dropdown();
147-
this.setCheckboxes();
148160
nextTick(() => {
149161
this.$refs.search.focus();
150162
});
163+
164+
this.hasMounted = true;
151165
},
152166

153167
methods: {
@@ -156,39 +170,6 @@ function initVueComponents(app) {
156170
this.updateHistory();
157171
},
158172

159-
setCheckboxes() {
160-
switch (this.archivedFilter) {
161-
case 'unarchived':
162-
$('#archivedFilterCheckbox').checkbox('set unchecked');
163-
break;
164-
case 'archived':
165-
$('#archivedFilterCheckbox').checkbox('set checked');
166-
break;
167-
case 'both':
168-
$('#archivedFilterCheckbox').checkbox('set indeterminate');
169-
break;
170-
default:
171-
this.archivedFilter = 'unarchived';
172-
$('#archivedFilterCheckbox').checkbox('set unchecked');
173-
break;
174-
}
175-
switch (this.privateFilter) {
176-
case 'public':
177-
$('#privateFilterCheckbox').checkbox('set unchecked');
178-
break;
179-
case 'private':
180-
$('#privateFilterCheckbox').checkbox('set checked');
181-
break;
182-
case 'both':
183-
$('#privateFilterCheckbox').checkbox('set indeterminate');
184-
break;
185-
default:
186-
this.privateFilter = 'both';
187-
$('#privateFilterCheckbox').checkbox('set indeterminate');
188-
break;
189-
}
190-
},
191-
192173
changeReposFilter(filter) {
193174
this.reposFilter = filter;
194175
this.repos = [];
@@ -245,45 +226,29 @@ function initVueComponents(app) {
245226
},
246227

247228
toggleArchivedFilter() {
248-
switch (this.archivedFilter) {
249-
case 'both':
250-
this.archivedFilter = 'unarchived';
251-
break;
252-
case 'unarchived':
253-
this.archivedFilter = 'archived';
254-
break;
255-
case 'archived':
256-
this.archivedFilter = 'both';
257-
break;
258-
default:
259-
this.archivedFilter = 'unarchived';
260-
break;
229+
if (this.archivedFilter === 'unarchived') {
230+
this.archivedFilter = 'archived';
231+
} else if (this.archivedFilter === 'archived') {
232+
this.archivedFilter = 'both';
233+
} else { // including both
234+
this.archivedFilter = 'unarchived';
261235
}
262236
this.page = 1;
263237
this.repos = [];
264-
this.setCheckboxes();
265238
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
266239
this.searchRepos();
267240
},
268241

269242
togglePrivateFilter() {
270-
switch (this.privateFilter) {
271-
case 'both':
272-
this.privateFilter = 'public';
273-
break;
274-
case 'public':
275-
this.privateFilter = 'private';
276-
break;
277-
case 'private':
278-
this.privateFilter = 'both';
279-
break;
280-
default:
281-
this.privateFilter = 'both';
282-
break;
243+
if (this.privateFilter === 'both') {
244+
this.privateFilter = 'public';
245+
} else if (this.privateFilter === 'public') {
246+
this.privateFilter = 'private';
247+
} else { // including private
248+
this.privateFilter = 'both';
283249
}
284250
this.page = 1;
285251
this.repos = [];
286-
this.setCheckboxes();
287252
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
288253
this.searchRepos();
289254
},

0 commit comments

Comments
 (0)