mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 06:38:31 +00:00 
			
		
		
		
	Rearrange Clone Panel (#31142)
Rearrange the clone panel to use less horizontal space. The following changes have been made to achieve this: - Moved everything into the dropdown menu - Moved the HTTPS/SSH Switch to a separate line - Moved the "Clone in VS Code"-Button up and added a divider - Named the dropdown button "Code", added appropriate icon --------- Co-authored-by: techknowlogick <techknowlogick@gitea.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -5,6 +5,8 @@ import {showErrorToast} from '../modules/toast.ts'; | ||||
| import {sleep} from '../utils.ts'; | ||||
| import RepoActivityTopAuthors from '../components/RepoActivityTopAuthors.vue'; | ||||
| import {createApp} from 'vue'; | ||||
| import {toOriginUrl} from '../utils/url.ts'; | ||||
| import {createTippy} from '../modules/tippy.ts'; | ||||
|  | ||||
| async function onDownloadArchive(e) { | ||||
|   e.preventDefault(); | ||||
| @@ -41,27 +43,68 @@ export function initRepoActivityTopAuthorsChart() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| export function initRepoCloneLink() { | ||||
|   const $repoCloneSsh = $('#repo-clone-ssh'); | ||||
|   const $repoCloneHttps = $('#repo-clone-https'); | ||||
|   const $inputLink = $('#repo-clone-url'); | ||||
| function initCloneSchemeUrlSelection(parent: Element) { | ||||
|   const elCloneUrlInput = parent.querySelector<HTMLInputElement>('.repo-clone-url'); | ||||
|  | ||||
|   if ((!$repoCloneSsh.length && !$repoCloneHttps.length) || !$inputLink.length) { | ||||
|     return; | ||||
|   } | ||||
|   const tabSsh = parent.querySelector('.repo-clone-ssh'); | ||||
|   const tabHttps = parent.querySelector('.repo-clone-https'); | ||||
|   const updateClonePanelUi = function() { | ||||
|     const scheme = localStorage.getItem('repo-clone-protocol') || 'https'; | ||||
|     const isSSH = scheme === 'ssh' && Boolean(tabSsh) || scheme !== 'ssh' && !tabHttps; | ||||
|     if (tabHttps) { | ||||
|       tabHttps.textContent = window.origin.split(':')[0].toUpperCase(); // show "HTTP" or "HTTPS" | ||||
|       tabHttps.classList.toggle('active', !isSSH); | ||||
|     } | ||||
|     if (tabSsh) { | ||||
|       tabSsh.classList.toggle('active', isSSH); | ||||
|     } | ||||
|  | ||||
|   $repoCloneSsh.on('click', () => { | ||||
|     const tab = isSSH ? tabSsh : tabHttps; | ||||
|     if (!tab) return; | ||||
|     const link = toOriginUrl(tab.getAttribute('data-link')); | ||||
|  | ||||
|     for (const el of document.querySelectorAll('.js-clone-url')) { | ||||
|       if (el.nodeName === 'INPUT') { | ||||
|         (el as HTMLInputElement).value = link; | ||||
|       } else { | ||||
|         el.textContent = link; | ||||
|       } | ||||
|     } | ||||
|     for (const el of parent.querySelectorAll<HTMLAnchorElement>('.js-clone-url-editor')) { | ||||
|       el.href = el.getAttribute('data-href-template').replace('{url}', encodeURIComponent(link)); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   updateClonePanelUi(); | ||||
|  | ||||
|   tabSsh.addEventListener('click', () => { | ||||
|     localStorage.setItem('repo-clone-protocol', 'ssh'); | ||||
|     window.updateCloneStates(); | ||||
|     updateClonePanelUi(); | ||||
|   }); | ||||
|   $repoCloneHttps.on('click', () => { | ||||
|   tabHttps.addEventListener('click', () => { | ||||
|     localStorage.setItem('repo-clone-protocol', 'https'); | ||||
|     window.updateCloneStates(); | ||||
|     updateClonePanelUi(); | ||||
|   }); | ||||
|   elCloneUrlInput.addEventListener('focus', () => { | ||||
|     elCloneUrlInput.select(); | ||||
|   }); | ||||
| } | ||||
|  | ||||
|   $inputLink.on('focus', () => { | ||||
|     $inputLink.trigger('select'); | ||||
| function initClonePanelButton(btn: HTMLButtonElement) { | ||||
|   const elPanel = btn.nextElementSibling; | ||||
|   createTippy(btn, { | ||||
|     content: elPanel, | ||||
|     trigger: 'click', | ||||
|     placement: 'bottom-end', | ||||
|     interactive: true, | ||||
|     hideOnClick: true, | ||||
|   }); | ||||
|   initCloneSchemeUrlSelection(elPanel); | ||||
| } | ||||
|  | ||||
| export function initRepoCloneButtons() { | ||||
|   queryElems(document, '.js-btn-clone-panel', initClonePanelButton); | ||||
|   queryElems(document, '.clone-buttons-combo', initCloneSchemeUrlSelection); | ||||
| } | ||||
|  | ||||
| export function initRepoCommonBranchOrTagDropdown(selector: string) { | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import { | ||||
| import {initUnicodeEscapeButton} from './repo-unicode-escape.ts'; | ||||
| import {initRepoBranchTagSelector} from '../components/RepoBranchTagSelector.vue'; | ||||
| import { | ||||
|   initRepoCloneLink, initRepoCommonBranchOrTagDropdown, initRepoCommonFilterSearchDropdown, | ||||
|   initRepoCloneButtons, initRepoCommonBranchOrTagDropdown, initRepoCommonFilterSearchDropdown, | ||||
| } from './repo-common.ts'; | ||||
| import {initCitationFileCopyContent} from './citation.ts'; | ||||
| import {initCompLabelEdit} from './comp/LabelEdit.ts'; | ||||
| @@ -54,7 +54,7 @@ export function initRepository() { | ||||
|     initRepoCommonFilterSearchDropdown('.choose.branch .dropdown'); | ||||
|   } | ||||
|  | ||||
|   initRepoCloneLink(); | ||||
|   initRepoCloneButtons(); | ||||
|   initCitationFileCopyContent(); | ||||
|   initRepoSettings(); | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import {pathEscapeSegments, isUrl} from './url.ts'; | ||||
| import {pathEscapeSegments, isUrl, toOriginUrl} from './url.ts'; | ||||
|  | ||||
| test('pathEscapeSegments', () => { | ||||
|   expect(pathEscapeSegments('a/b/c')).toEqual('a/b/c'); | ||||
| @@ -11,3 +11,19 @@ test('isUrl', () => { | ||||
|   expect(isUrl('https://example.com/index.html')).toEqual(true); | ||||
|   expect(isUrl('/index.html')).toEqual(false); | ||||
| }); | ||||
|  | ||||
| test('toOriginUrl', () => { | ||||
|   const oldLocation = String(window.location); | ||||
|   for (const origin of ['https://example.com', 'https://example.com:3000']) { | ||||
|     window.location.assign(`${origin}/`); | ||||
|     expect(toOriginUrl('/')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('/org/repo.git')).toEqual(`${origin}/org/repo.git`); | ||||
|     expect(toOriginUrl('https://another.com')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com/')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com/org/repo.git')).toEqual(`${origin}/org/repo.git`); | ||||
|     expect(toOriginUrl('https://another.com:4000')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com:4000/')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com:4000/org/repo.git')).toEqual(`${origin}/org/repo.git`); | ||||
|   } | ||||
|   window.location.assign(oldLocation); | ||||
| }); | ||||
|   | ||||
| @@ -13,3 +13,19 @@ export function isUrl(url: string): boolean { | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Convert an absolute or relative URL to an absolute URL with the current origin. It only | ||||
| // processes absolute HTTP/HTTPS URLs or relative URLs like '/xxx' or '//host/xxx'. | ||||
| export function toOriginUrl(urlStr: string) { | ||||
|   try { | ||||
|     if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { | ||||
|       const {origin, protocol, hostname, port} = window.location; | ||||
|       const url = new URL(urlStr, origin); | ||||
|       url.protocol = protocol; | ||||
|       url.hostname = hostname; | ||||
|       url.port = port || (protocol === 'https:' ? '443' : '80'); | ||||
|       return url.toString(); | ||||
|     } | ||||
|   } catch {} | ||||
|   return urlStr; | ||||
| } | ||||
|   | ||||
| @@ -1,17 +0,0 @@ | ||||
| import {toOriginUrl} from './origin-url.ts'; | ||||
|  | ||||
| test('toOriginUrl', () => { | ||||
|   const oldLocation = String(window.location); | ||||
|   for (const origin of ['https://example.com', 'https://example.com:3000']) { | ||||
|     window.location.assign(`${origin}/`); | ||||
|     expect(toOriginUrl('/')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('/org/repo.git')).toEqual(`${origin}/org/repo.git`); | ||||
|     expect(toOriginUrl('https://another.com')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com/')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com/org/repo.git')).toEqual(`${origin}/org/repo.git`); | ||||
|     expect(toOriginUrl('https://another.com:4000')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com:4000/')).toEqual(`${origin}/`); | ||||
|     expect(toOriginUrl('https://another.com:4000/org/repo.git')).toEqual(`${origin}/org/repo.git`); | ||||
|   } | ||||
|   window.location.assign(oldLocation); | ||||
| }); | ||||
| @@ -1,19 +1,4 @@ | ||||
| // Convert an absolute or relative URL to an absolute URL with the current origin. It only | ||||
| // processes absolute HTTP/HTTPS URLs or relative URLs like '/xxx' or '//host/xxx'. | ||||
| // NOTE: Keep this function in sync with clone_script.tmpl | ||||
| export function toOriginUrl(urlStr: string) { | ||||
|   try { | ||||
|     if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { | ||||
|       const {origin, protocol, hostname, port} = window.location; | ||||
|       const url = new URL(urlStr, origin); | ||||
|       url.protocol = protocol; | ||||
|       url.hostname = hostname; | ||||
|       url.port = port || (protocol === 'https:' ? '443' : '80'); | ||||
|       return url.toString(); | ||||
|     } | ||||
|   } catch {} | ||||
|   return urlStr; | ||||
| } | ||||
| import {toOriginUrl} from '../utils/url.ts'; | ||||
|  | ||||
| window.customElements.define('origin-url', class extends HTMLElement { | ||||
|   connectedCallback() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user