summaryrefslogtreecommitdiff
path: root/source/features/more-dropdown.tsx
blob: f78ce06d147fbe25e1ee0adcf40c6b1527128153 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import './more-dropdown.css';
import React from 'dom-chef';
import select from 'select-dom';
import elementReady from 'element-ready';
import * as pageDetect from 'github-url-detection';

import DiffIcon from 'octicon/diff.svg';
import BranchIcon from 'octicon/git-branch.svg';
import HistoryIcon from 'octicon/history.svg';
import PackageIcon from 'octicon/package.svg';

import features from '.';
import {appendBefore} from '../helpers/dom-utils';
import getDefaultBranch from '../github-helpers/get-default-branch';
import {buildRepoURL, getCurrentBranch} from '../github-helpers';

function createDropdown(): void {
	// Markup copied from native GHE dropdown
	appendBefore(
		// GHE doesn't have `reponav > ul`
		select('.reponav > ul') ?? select('.reponav')!,
		'[data-selected-links^="repo_settings"]',
		<details className="reponav-dropdown details-overlay details-reset">
			<summary className="btn-link reponav-item" aria-haspopup="menu">
				{'More '}
				<span className="dropdown-caret"/>
			</summary>
			<details-menu className="dropdown-menu dropdown-menu-se"/>
		</details>
	);
}

/* eslint-disable-next-line import/prefer-default-export */
export function createDropdownItem(label: string, url: string, attributes?: Record<string, string>): Element {
	return (
		<li {...attributes}>
			<a role="menuitem" className="dropdown-item" href={url}>
				{label}
			</a>
		</li>
	);
}

function onlyShowInDropdown(id: string): void {
	select(`[data-tab-item="${id}"]`)!.parentElement!.remove();
	const menuItem = select(`[data-menu-item="${id}"]`)!;
	menuItem.removeAttribute('data-menu-item');
	menuItem.hidden = false;

	// The item has to be moved somewhere else because the overflow nav is order-dependent
	select('.js-responsive-underlinenav-overflow ul')!.append(menuItem);
}

async function init(): Promise<void> {
	// Wait for the tab bar to be loaded
	await elementReady([
		'.pagehead + *', // Pre "Repository refresh" layout
		'.UnderlineNav-body + *'
	].join());

	const reference = getCurrentBranch() ?? await getDefaultBranch();
	const compareUrl = buildRepoURL('compare', reference);
	const commitsUrl = buildRepoURL('commits', reference);
	const branchesUrl = buildRepoURL('branches');
	const dependenciesUrl = buildRepoURL('network/dependencies');

	const nav = select('.js-responsive-underlinenav .UnderlineNav-body');
	if (nav) {
		// "Repository refresh" layout
		nav.parentElement!.classList.add('rgh-has-more-dropdown');
		select('.js-responsive-underlinenav-overflow ul')!.append(
			<li className="dropdown-divider" role="separator"/>,
			createDropdownItem('Compare', compareUrl),
			pageDetect.isEnterprise() ? '' : createDropdownItem('Dependencies', dependenciesUrl),
			createDropdownItem('Commits', commitsUrl),
			createDropdownItem('Branches', branchesUrl)
		);

		onlyShowInDropdown('security-tab');
		onlyShowInDropdown('insights-tab');
		return;
	}

	// Pre "Repository refresh" layout
	if (!select.exists('.reponav-dropdown')) {
		createDropdown();
	}

	const menu = select('.reponav-dropdown .dropdown-menu')!;
	menu.append(
		<a href={compareUrl} className="rgh-reponav-more dropdown-item">
			<DiffIcon/> Compare
		</a>,

		pageDetect.isEnterprise() ? '' : (
			<a href={dependenciesUrl} className="rgh-reponav-more dropdown-item">
				<PackageIcon/> Dependencies
			</a>
		),

		<a href={commitsUrl} className="rgh-reponav-more dropdown-item">
			<HistoryIcon/> Commits
		</a>,

		<a href={branchesUrl} className="rgh-reponav-more dropdown-item">
			<BranchIcon/> Branches
		</a>
	);

	// Selector only affects desktop navigation
	for (const tab of select.all<HTMLAnchorElement>(`
		.hx_reponav [data-selected-links~="pulse"],
		.hx_reponav [data-selected-links~="security"]
	`)) {
		tab.remove();
		menu.append(
			<a href={tab.href} className="rgh-reponav-more dropdown-item">
				{[...tab.childNodes]}
			</a>
		);
	}
}

void features.add(__filebasename, {
	include: [
		pageDetect.isRepo
	],
	awaitDomReady: false,
	init
});