mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 11:41:32 +01: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:
		
							parent
							
								
									8a53a39c42
								
							
						
					
					
						commit
						18061af490
					
				| @ -74,9 +74,9 @@ func prepareOpenWithEditorApps(ctx *context.Context) { | ||||
| 		schema, _, _ := strings.Cut(app.OpenURL, ":") | ||||
| 		var iconHTML template.HTML | ||||
| 		if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" { | ||||
| 			iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-%s", schema), 16, "tw-mr-2") | ||||
| 			iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-%s", schema), 16) | ||||
| 		} else { | ||||
| 			iconHTML = svg.RenderHTML("gitea-git", 16, "tw-mr-2") // TODO: it could support user's customized icon in the future | ||||
| 			iconHTML = svg.RenderHTML("gitea-git", 16) // TODO: it could support user's customized icon in the future | ||||
| 		} | ||||
| 		tmplApps = append(tmplApps, map[string]any{ | ||||
| 			"DisplayName": app.DisplayName, | ||||
|  | ||||
| @ -1,15 +1,13 @@ | ||||
| <!-- there is always at least one button (by context/repo.go) --> | ||||
| {{if $.CloneButtonShowHTTPS}} | ||||
| 	<button class="ui small button" id="repo-clone-https" data-link="{{$.CloneButtonOriginLink.HTTPS}}"> | ||||
| 		HTTPS | ||||
| <!-- there is always at least one button (guaranteed by context/repo.go) --> | ||||
| <div class="ui action small input clone-buttons-combo"> | ||||
| 	{{if $.CloneButtonShowHTTPS}} | ||||
| 		<button class="ui small button repo-clone-https" data-link="{{$.CloneButtonOriginLink.HTTPS}}">HTTPS</button> | ||||
| 	{{end}} | ||||
| 	{{if $.CloneButtonShowSSH}} | ||||
| 		<button class="ui small button repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}">SSH</button> | ||||
| 	{{end}} | ||||
| 	<input size="10" class="repo-clone-url js-clone-url" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly> | ||||
| 	<button class="ui small icon button" data-clipboard-target=".repo-clone-url" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}"> | ||||
| 		{{svg "octicon-copy" 14}} | ||||
| 	</button> | ||||
| {{end}} | ||||
| {{if $.CloneButtonShowSSH}} | ||||
| 	<button class="ui small button" id="repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}"> | ||||
| 		SSH | ||||
| 	</button> | ||||
| {{end}} | ||||
| <input id="repo-clone-url" size="10" class="js-clone-url" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly> | ||||
| <button class="ui small icon button" id="clipboard-btn" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}" data-clipboard-target="#repo-clone-url" aria-label="{{ctx.Locale.Tr "copy_url"}}"> | ||||
| 	{{svg "octicon-copy" 14}} | ||||
| </button> | ||||
| </div> | ||||
|  | ||||
							
								
								
									
										44
									
								
								templates/repo/clone_panel.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								templates/repo/clone_panel.tmpl
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| <button class="ui green button js-btn-clone-panel"> | ||||
| 	<span>{{svg "octicon-code" 16}} Code</span> | ||||
| 	{{svg "octicon-triangle-down" 14 "dropdown icon"}} | ||||
| </button> | ||||
| <div class="clone-panel-popup tippy-target"> | ||||
| 	<div class="flex-text-block clone-panel-field">{{svg "octicon-terminal"}} Clone</div> | ||||
| 
 | ||||
| 	<div class="clone-panel-tab"> | ||||
| 		<!-- there is always at least one button (guaranteed by context/repo.go) --> | ||||
| 		{{if $.CloneButtonShowHTTPS}} | ||||
| 			<button class="item repo-clone-https" data-link="{{$.CloneButtonOriginLink.HTTPS}}">HTTPS</button> | ||||
| 		{{end}} | ||||
| 		{{if $.CloneButtonShowSSH}} | ||||
| 			<button class="item repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}">SSH</button> | ||||
| 		{{end}} | ||||
| 	</div> | ||||
| 	<div class="divider"></div> | ||||
| 
 | ||||
| 	<div class="clone-panel-field"> | ||||
| 		<div class="ui input tiny action"> | ||||
| 			<input size="30" class="repo-clone-url js-clone-url" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly> | ||||
| 			<div class="ui small compact icon button" data-clipboard-target=".js-clone-url" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}"> | ||||
| 				{{svg "octicon-copy" 14}} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 
 | ||||
| 	{{if not .PageIsWiki}} | ||||
| 		<div class="flex-items-block clone-panel-list"> | ||||
| 			{{range .OpenWithEditorApps}} | ||||
| 			<a class="item muted js-clone-url-editor" data-href-template="{{.OpenURL}}">{{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}}</a> | ||||
| 			{{end}} | ||||
| 		</div> | ||||
| 
 | ||||
| 		{{if and (not $.DisableDownloadSourceArchives) $.RefName}} | ||||
| 		<div class="divider"></div> | ||||
| 		<div class="flex-items-block clone-panel-list"> | ||||
| 				<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a> | ||||
| 				<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a> | ||||
| 				<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a> | ||||
| 		</div> | ||||
| 		{{end}} | ||||
| 	{{end}} | ||||
| </div> | ||||
| @ -1,50 +0,0 @@ | ||||
| <script> | ||||
| 	// synchronously set clone button states and urls here to avoid flickering | ||||
| 	// on page load. initRepoCloneLink calls this when proto changes. | ||||
| 	// this applies the protocol-dependant clone url to all elements with the | ||||
| 	// `js-clone-url` and `js-clone-url-vsc` classes. | ||||
| 	// TODO: This localStorage setting should be moved to backend user config | ||||
| 	// so it's available during rendering, then this inline script can be removed. | ||||
| 	(window.updateCloneStates = function() { | ||||
| 		const httpsBtn = document.getElementById('repo-clone-https'); | ||||
| 		const sshBtn = document.getElementById('repo-clone-ssh'); | ||||
| 		const value = localStorage.getItem('repo-clone-protocol') || 'https'; | ||||
| 		const isSSH = value === 'ssh' && sshBtn || value !== 'ssh' && !httpsBtn; | ||||
| 
 | ||||
| 		if (httpsBtn) { | ||||
| 			httpsBtn.textContent = window.origin.split(':')[0].toUpperCase(); | ||||
| 			httpsBtn.classList.toggle('primary', !isSSH); | ||||
| 			httpsBtn.classList.toggle('basic', isSSH); | ||||
| 		} | ||||
| 		if (sshBtn) { | ||||
| 			sshBtn.classList.toggle('primary', isSSH); | ||||
| 			sshBtn.classList.toggle('basic', !isSSH); | ||||
| 		} | ||||
| 
 | ||||
| 		const btn = isSSH ? sshBtn : httpsBtn; | ||||
| 		if (!btn) return; | ||||
| 
 | ||||
| 		// NOTE: Keep this function in sync with the one in the js folder | ||||
| 		function toOriginUrl(urlStr) { | ||||
| 			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; | ||||
| 		} | ||||
| 		const link = toOriginUrl(btn.getAttribute('data-link')); | ||||
| 
 | ||||
| 		for (const el of document.getElementsByClassName('js-clone-url')) { | ||||
| 			el[el.nodeName === 'INPUT' ? 'value' : 'textContent'] = link; | ||||
| 		} | ||||
| 		for (const el of document.getElementsByClassName('js-clone-url-editor')) { | ||||
| 			el.href = el.getAttribute('data-href-template').replace('{url}', encodeURIComponent(link)); | ||||
| 		} | ||||
| 	})(); | ||||
| </script> | ||||
| @ -37,9 +37,7 @@ | ||||
| 									</a> | ||||
| 									{{end}} | ||||
| 								{{end}} | ||||
| 								<div class="clone-panel ui action small input tw-flex-1"> | ||||
| 									{{template "repo/clone_buttons" .}} | ||||
| 								</div> | ||||
| 								{{template "repo/clone_buttons" .}} | ||||
| 							</div> | ||||
| 						</div> | ||||
| 
 | ||||
| @ -73,7 +71,6 @@ git push -u origin {{.Repository.DefaultBranch}}</code></pre> | ||||
| 							{{ctx.Locale.Tr "repo.empty_message"}} | ||||
| 						</div> | ||||
| 					{{end}} | ||||
| 					{{template "repo/clone_script" .}} | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
|  | ||||
| @ -106,23 +106,7 @@ | ||||
| 					<div class="repo-button-row-right {{if not $isTreePathRoot}}tw-flex-grow-0{{end}}"> | ||||
| 						<!-- Only show clone panel in repository home page --> | ||||
| 						{{if $isTreePathRoot}} | ||||
| 							<div class="clone-panel ui action tiny input"> | ||||
| 								{{template "repo/clone_buttons" .}} | ||||
| 								<button class="ui small jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}"> | ||||
| 									{{svg "octicon-kebab-horizontal"}} | ||||
| 									<div class="menu"> | ||||
| 										{{if not $.DisableDownloadSourceArchives}} | ||||
| 											<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_zip"}}</a> | ||||
| 											<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_tar"}}</a> | ||||
| 											<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_bundle"}}</a> | ||||
| 										{{end}} | ||||
| 										{{range .OpenWithEditorApps}} | ||||
| 											<a class="item js-clone-url-editor" data-href-template="{{.OpenURL}}">{{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}}</a> | ||||
| 										{{end}} | ||||
| 									</div> | ||||
| 								</button> | ||||
| 								{{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}} | ||||
| 							</div> | ||||
| 							{{template "repo/clone_panel" .}} | ||||
| 						{{end}} | ||||
| 						{{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}} | ||||
| 							<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}"> | ||||
|  | ||||
| @ -15,10 +15,7 @@ | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="ui eight wide column text right"> | ||||
| 				<div class="clone-panel ui action small input"> | ||||
| 					{{template "repo/clone_buttons" .}} | ||||
| 					{{template "repo/clone_script" .}} | ||||
| 				</div> | ||||
| 				{{template "repo/clone_panel" .}} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<h2 class="ui top header">{{ctx.Locale.Tr "repo.wiki.wiki_page_revisions"}}</h2> | ||||
|  | ||||
| @ -28,10 +28,7 @@ | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="clone-panel ui action small input"> | ||||
| 				{{template "repo/clone_buttons" .}} | ||||
| 				{{template "repo/clone_script" .}} | ||||
| 			</div> | ||||
| 			{{template "repo/clone_panel" .}} | ||||
| 		</div> | ||||
| 		<div class="ui dividing header"> | ||||
| 			<div class="flex-text-block tw-flex-wrap tw-justify-end"> | ||||
|  | ||||
| @ -127,10 +127,10 @@ func TestViewRepo1CloneLinkAnonymous(t *testing.T) { | ||||
| 	resp := MakeRequest(t, req, http.StatusOK) | ||||
| 
 | ||||
| 	htmlDoc := NewHTMLParser(t, resp.Body) | ||||
| 	link, exists := htmlDoc.doc.Find("#repo-clone-https").Attr("data-link") | ||||
| 	link, exists := htmlDoc.doc.Find(".repo-clone-https").Attr("data-link") | ||||
| 	assert.True(t, exists, "The template has changed") | ||||
| 	assert.Equal(t, setting.AppURL+"user2/repo1.git", link) | ||||
| 	_, exists = htmlDoc.doc.Find("#repo-clone-ssh").Attr("data-link") | ||||
| 	_, exists = htmlDoc.doc.Find(".repo-clone-ssh").Attr("data-link") | ||||
| 	assert.False(t, exists) | ||||
| } | ||||
| 
 | ||||
| @ -143,10 +143,10 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) { | ||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||
| 
 | ||||
| 	htmlDoc := NewHTMLParser(t, resp.Body) | ||||
| 	link, exists := htmlDoc.doc.Find("#repo-clone-https").Attr("data-link") | ||||
| 	link, exists := htmlDoc.doc.Find(".repo-clone-https").Attr("data-link") | ||||
| 	assert.True(t, exists, "The template has changed") | ||||
| 	assert.Equal(t, setting.AppURL+"user2/repo1.git", link) | ||||
| 	link, exists = htmlDoc.doc.Find("#repo-clone-ssh").Attr("data-link") | ||||
| 	link, exists = htmlDoc.doc.Find(".repo-clone-ssh").Attr("data-link") | ||||
| 	assert.True(t, exists, "The template has changed") | ||||
| 	sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.SSH.User, setting.SSH.Domain, setting.SSH.Port) | ||||
| 	assert.Equal(t, sshURL, link) | ||||
|  | ||||
| @ -67,6 +67,7 @@ | ||||
| @import "./repo/header.css"; | ||||
| @import "./repo/home.css"; | ||||
| @import "./repo/reactions.css"; | ||||
| @import "./repo/clone.css"; | ||||
| 
 | ||||
| @import "./editor/fileeditor.css"; | ||||
| @import "./editor/combomarkdowneditor.css"; | ||||
|  | ||||
| @ -101,42 +101,6 @@ | ||||
|   margin-bottom: 12px; | ||||
| } | ||||
| 
 | ||||
| .repository .clone-panel { | ||||
|   display: flex; | ||||
|   flex: 1; | ||||
| } | ||||
| 
 | ||||
| .repository.wiki .clone-panel { | ||||
|   flex: 0; | ||||
| } | ||||
| 
 | ||||
| .repository.wiki .clone-panel input { | ||||
|   width: 20ch; | ||||
| } | ||||
| 
 | ||||
| .repository .clone-panel #repo-clone-url { | ||||
|   border-radius: 0; | ||||
|   flex: 1; | ||||
| } | ||||
| 
 | ||||
| .repository .ui.action.input.clone-panel > button + button, | ||||
| .repository .ui.action.input.clone-panel > button + input { | ||||
|   margin-left: -1px; /* make the borders overlap to avoid double borders */ | ||||
| } | ||||
| 
 | ||||
| .repository .clone-panel > button:first-of-type { | ||||
|   border-radius: var(--border-radius) 0 0 var(--border-radius) !important; | ||||
| } | ||||
| 
 | ||||
| .repository .clone-panel > button:last-of-type { | ||||
|   border-radius: 0 var(--border-radius) var(--border-radius) 0 !important; | ||||
| } | ||||
| 
 | ||||
| .repository .clone-panel .dropdown .menu { | ||||
|   right: 0 !important; | ||||
|   left: auto !important; | ||||
| } | ||||
| 
 | ||||
| .repository .repo-description { | ||||
|   font-size: 16px; | ||||
|   margin-bottom: 5px; | ||||
| @ -1615,14 +1579,6 @@ td .commit-summary { | ||||
|   font-weight: var(--font-weight-normal); | ||||
| } | ||||
| 
 | ||||
| .repository.quickstart .guide #repo-clone-url { | ||||
|   border-radius: 0; | ||||
|   padding: 5px 10px; | ||||
|   font-size: 1.2em; | ||||
|   line-height: 1.4; | ||||
|   flex: 1 | ||||
| } | ||||
| 
 | ||||
| .empty-placeholder { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|  | ||||
							
								
								
									
										32
									
								
								web_src/css/repo/clone.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web_src/css/repo/clone.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| /* only used by "repo/empty.tmpl" */ | ||||
| .clone-buttons-combo { | ||||
|   flex: 1; | ||||
| } | ||||
| 
 | ||||
| .clone-buttons-combo input { | ||||
|   border-left: none !important; | ||||
|   border-radius: 0 !important; | ||||
| } | ||||
| 
 | ||||
| /* used by the clone-panel popup */ | ||||
| .clone-panel-field, | ||||
| .clone-panel-list { | ||||
|   margin: 10px; | ||||
| } | ||||
| 
 | ||||
| .clone-panel-tab .item { | ||||
|   padding: 5px 10px; | ||||
|   background: none; | ||||
| } | ||||
| 
 | ||||
| .clone-panel-tab .item.active { | ||||
|   border-bottom: 3px solid var(--color-secondary); | ||||
| } | ||||
| 
 | ||||
| .clone-panel-tab + .divider { | ||||
|   margin: -1px 0 0; | ||||
| } | ||||
| 
 | ||||
| .clone-panel-list .item { | ||||
|   margin: 5px 0; | ||||
| } | ||||
| @ -59,9 +59,6 @@ | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 767.98px) { | ||||
|   .repository.wiki .clone-panel #repo-clone-url { | ||||
|     width: 160px; | ||||
|   } | ||||
|   .repository.wiki .wiki-content-main.with-sidebar, | ||||
|   .repository.wiki .wiki-content-sidebar { | ||||
|     float: none; | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user