aboutsummaryrefslogtreecommitdiffstats
path: root/web_src/js
diff options
context:
space:
mode:
authorBlender Defender <contact.blenderdefender@gmail.com>2024-12-11 14:54:30 +0100
committerGitHub <noreply@github.com>2024-12-11 21:54:30 +0800
commit18061af49068c8fcceb316f889d719bff6ba8155 (patch)
tree1807e097a933379a4bd513b68118815f09770b24 /web_src/js
parent8a53a39c426e95164e87a3b0857eb420711bfd85 (diff)
downloadgitea-18061af49068c8fcceb316f889d719bff6ba8155.tar.gz
gitea-18061af49068c8fcceb316f889d719bff6ba8155.zip
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>
Diffstat (limited to 'web_src/js')
-rw-r--r--web_src/js/features/repo-common.ts69
-rw-r--r--web_src/js/features/repo-legacy.ts4
-rw-r--r--web_src/js/utils/url.test.ts18
-rw-r--r--web_src/js/utils/url.ts16
-rw-r--r--web_src/js/webcomponents/origin-url.test.ts17
-rw-r--r--web_src/js/webcomponents/origin-url.ts17
6 files changed, 92 insertions, 49 deletions
diff --git a/web_src/js/features/repo-common.ts b/web_src/js/features/repo-common.ts
index 5185a7ca43..336deb125f 100644
--- a/web_src/js/features/repo-common.ts
+++ b/web_src/js/features/repo-common.ts
@@ -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);
+ }
+
+ const tab = isSSH ? tabSsh : tabHttps;
+ if (!tab) return;
+ const link = toOriginUrl(tab.getAttribute('data-link'));
- $repoCloneSsh.on('click', () => {
+ 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) {
diff --git a/web_src/js/features/repo-legacy.ts b/web_src/js/features/repo-legacy.ts
index dfea66c7ad..2f760f1d15 100644
--- a/web_src/js/features/repo-legacy.ts
+++ b/web_src/js/features/repo-legacy.ts
@@ -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();
diff --git a/web_src/js/utils/url.test.ts b/web_src/js/utils/url.test.ts
index 25fda79b19..bb331a6b49 100644
--- a/web_src/js/utils/url.test.ts
+++ b/web_src/js/utils/url.test.ts
@@ -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);
+});
diff --git a/web_src/js/utils/url.ts b/web_src/js/utils/url.ts
index c5a28774a9..a7d61c5e83 100644
--- a/web_src/js/utils/url.ts
+++ b/web_src/js/utils/url.ts
@@ -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;
+}
diff --git a/web_src/js/webcomponents/origin-url.test.ts b/web_src/js/webcomponents/origin-url.test.ts
deleted file mode 100644
index 19cc467d7d..0000000000
--- a/web_src/js/webcomponents/origin-url.test.ts
+++ /dev/null
@@ -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);
-});
diff --git a/web_src/js/webcomponents/origin-url.ts b/web_src/js/webcomponents/origin-url.ts
index d407fe0dff..dbb910ce6c 100644
--- a/web_src/js/webcomponents/origin-url.ts
+++ b/web_src/js/webcomponents/origin-url.ts
@@ -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() {