blob: 6d2fe20fe0809706dd617a2af190accc2b5f1e82 [file] [log] [blame]
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../gr-labeled-autocomplete/gr-labeled-autocomplete';
import '../gr-icon/gr-icon';
import {singleDecodeURL} from '../../../utils/url-util';
import {AutocompleteQuery} from '../gr-autocomplete/gr-autocomplete';
import {
BranchName,
RepoName,
ProjectInfoWithName,
BranchInfo,
} from '../../../types/common';
import {GrLabeledAutocomplete} from '../gr-labeled-autocomplete/gr-labeled-autocomplete';
import {getAppContext} from '../../../services/app-context';
import {sharedStyles} from '../../../styles/shared-styles';
import {LitElement, PropertyValues, html, css} from 'lit';
import {customElement, property, state, query} from 'lit/decorators.js';
import {assertIsDefined} from '../../../utils/common-util';
import {fire} from '../../../utils/event-util';
import {BindValueChangeEvent} from '../../../types/events';
import {throwingErrorCallback} from '../gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const SUGGESTIONS_LIMIT = 15;
const REF_PREFIX = 'refs/heads/';
@customElement('gr-repo-branch-picker')
export class GrRepoBranchPicker extends LitElement {
@query('#repoInput') protected repoInput?: GrLabeledAutocomplete;
@query('#branchInput') protected branchInput?: GrLabeledAutocomplete;
@property({type: String})
repo?: RepoName;
@property({type: String})
branch?: BranchName;
@state() private branchDisabled = false;
private readonly query: AutocompleteQuery = () => Promise.resolve([]);
private readonly repoQuery: AutocompleteQuery = () => Promise.resolve([]);
private readonly restApiService = getAppContext().restApiService;
constructor() {
super();
this.query = input => this.getRepoBranchesSuggestions(input);
this.repoQuery = input => this.getRepoSuggestions(input);
}
override connectedCallback() {
super.connectedCallback();
if (this.repo) {
assertIsDefined(this.repoInput, 'repoInput');
this.repoInput.setText(this.repo);
}
this.branchDisabled = !this.repo;
}
static override get styles() {
return [
sharedStyles,
css`
:host {
display: block;
}
gr-labeled-autocomplete {
display: inline-block;
}
gr-icon {
margin-bottom: var(--spacing-l);
}
`,
];
}
override render() {
return html`
<div>
<gr-labeled-autocomplete
id="repoInput"
label="Repository"
placeholder="Select repo"
.query=${this.repoQuery}
@commit=${(e: CustomEvent<{value: string}>) => {
this.repoCommitted(e);
}}
>
</gr-labeled-autocomplete>
<gr-icon icon="chevron_right"></gr-icon>
<gr-labeled-autocomplete
id="branchInput"
label="Branch"
placeholder="Select branch"
?disabled=${this.branchDisabled}
.query=${this.query}
@commit=${(e: CustomEvent<{value: string}>) => {
this.branchCommitted(e);
}}
>
</gr-labeled-autocomplete>
</div>
`;
}
override willUpdate(changedProperties: PropertyValues) {
if (changedProperties.has('repo')) {
this.repoChanged();
}
}
// private but used in test
getRepoBranchesSuggestions(input: string) {
if (!this.repo) {
return Promise.resolve([]);
}
if (input.startsWith(REF_PREFIX)) {
input = input.substring(REF_PREFIX.length);
}
return this.restApiService
.getRepoBranches(
input,
this.repo,
SUGGESTIONS_LIMIT,
/* offset=*/ undefined,
throwingErrorCallback
)
.then(res => this.branchResponseToSuggestions(res));
}
private branchResponseToSuggestions(res: BranchInfo[] | undefined) {
if (!res) return [];
return res
.filter(branchInfo => branchInfo.ref !== 'HEAD')
.map(branchInfo => {
let branch;
if (branchInfo.ref.startsWith(REF_PREFIX)) {
branch = branchInfo.ref.substring(REF_PREFIX.length);
} else {
branch = branchInfo.ref;
}
return {name: branch, value: branch};
});
}
// private but used in test
getRepoSuggestions(input: string) {
return this.restApiService
.getRepos(
input,
SUGGESTIONS_LIMIT,
/* offset=*/ undefined,
throwingErrorCallback
)
.then(res => this.repoResponseToSuggestions(res));
}
private repoResponseToSuggestions(res: ProjectInfoWithName[] | undefined) {
if (!res) return [];
return res.map(repo => {
return {
name: repo.name,
value: singleDecodeURL(repo.id),
};
});
}
private repoCommitted(e: CustomEvent<{value: string}>) {
this.repo = e.detail.value as RepoName;
fire(this, 'repo-changed', {value: e.detail.value});
}
private branchCommitted(e: CustomEvent<{value: string}>) {
this.branch = e.detail.value as BranchName;
fire(this, 'branch-changed', {value: e.detail.value});
}
private repoChanged() {
assertIsDefined(this.branchInput, 'branchInput');
this.branchInput.clear();
this.branchDisabled = !this.repo;
}
}
declare global {
interface HTMLElementEventMap {
'branch-changed': BindValueChangeEvent;
'repo-changed': BindValueChangeEvent;
}
interface HTMLElementTagNameMap {
'gr-repo-branch-picker': GrRepoBranchPicker;
}
}