mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	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  #### Click "Archived" to make it checked  #### Click "Archived" to make it intermediate  #### Click "Archived" to make it unchecked  --------- Co-authored-by: yp05327 <576951401@qq.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		| @@ -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(); | ||||||
|       }, |       }, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 wxiaoguang
					wxiaoguang