blob: fdd7502d17fa456455c7b1d3d1449380be7f40e1 [file] [log] [blame]
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {css, html, LitElement, nothing} from 'lit';
import {customElement, state} from 'lit/decorators';
import {bulkActionsModelToken} from '../../../models/bulk-actions/bulk-actions-model';
import {resolve} from '../../../models/dependency';
import {pluralize} from '../../../utils/string-util';
import {subscribe} from '../../lit/subscription-controller';
import '../../shared/gr-button/gr-button';
import '../gr-change-list-bulk-abandon-flow/gr-change-list-bulk-abandon-flow';
import '../gr-change-list-reviewer-flow/gr-change-list-reviewer-flow';
import '../gr-change-list-bulk-vote-flow/gr-change-list-bulk-vote-flow';
/**
* An action bar for the top of a <gr-change-list-section> element. Assumes it
* will be used inside a <tr> element.
*/
@customElement('gr-change-list-action-bar')
export class GrChangeListActionBar extends LitElement {
static override get styles() {
return css`
:host {
display: contents;
}
.container {
display: flex;
justify-content: space-between;
align-items: center;
}
/*
* checkbox styles match checkboxes in <gr-change-list-item> rows to
* vertically align with them.
*/
input {
background-color: var(--background-color-primary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
box-sizing: border-box;
color: var(--primary-text-color);
margin: 0px;
padding: var(--spacing-s);
vertical-align: middle;
}
`;
}
@state()
private numSelected = 0;
@state()
private totalChangeCount = 0;
private readonly getBulkActionsModel = resolve(this, bulkActionsModelToken);
override connectedCallback(): void {
super.connectedCallback();
subscribe(
this,
this.getBulkActionsModel().selectedChangeNums$,
selectedChangeNums => (this.numSelected = selectedChangeNums.length)
);
subscribe(
this,
this.getBulkActionsModel().totalChangeCount$,
totalChangeCount => (this.totalChangeCount = totalChangeCount)
);
}
override render() {
const numSelectedLabel = `${pluralize(
this.numSelected,
'change'
)} selected`;
const checked =
this.numSelected > 0 && this.numSelected === this.totalChangeCount;
const indeterminate =
this.numSelected > 0 && this.numSelected !== this.totalChangeCount;
return html`
<!-- Empty cell added for spacing just like gr-change-list-item rows -->
<td></td>
<td>
<!--
The .checked property must be used rather than the attribute because
the attribute only controls the default checked state and does not
update the current checked state.
See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#attr-checked
-->
<input
type="checkbox"
.checked=${checked}
.indeterminate=${indeterminate}
@click=${() => this.getBulkActionsModel().clearSelectedChangeNums()}
/>
</td>
<!--
500 chosen to be more than the actual number of columns but less than
1000 where the browser apparently decides it is an error and reverts
back to colspan="1"
-->
<td colspan="500">
<div class="container">
<div class="selectionInfo">
${this.numSelected
? html`<span>${numSelectedLabel}</span>`
: nothing}
</div>
<div class="actionButtons">
<gr-change-list-bulk-vote-flow></gr-change-list-bulk-vote-flow>
<gr-change-list-reviewer-flow></gr-change-list-reviewer-flow>
<gr-change-list-bulk-abandon-flow>
</gr-change-list-bulk-abandon-flow>
</div>
</div>
</td>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'gr-change-list-action-bar': GrChangeListActionBar;
}
}