blob: 7a368db9f3050c118ae91f570464e4703405c268 [file] [log] [blame] [edit]
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../../elements/shared/gr-button/gr-button';
import {html, LitElement} from 'lit';
import {property, state} from 'lit/decorators.js';
import {DiffViewMode} from '../../../api/diff';
import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
import {getShowConfig, showAbove, showBelow} from './gr-context-controls';
import {ifDefined} from 'lit/directives/if-defined.js';
import {when} from 'lit/directives/when.js';
import {subscribe} from '../../../elements/lit/subscription-controller';
import {resolve} from '../../../models/dependency';
import {
ColumnsToShow,
diffModelToken,
NO_COLUMNS,
} from '../gr-diff-model/gr-diff-model';
/**
* This is a <tbody> diff section corresponding to a diff group of type
* CONTEXT_CONTROL. It typically contains three table rows: One padding row
* each at the top and at the bottom. The middle row contains the table cell
* (spanning mostly the entire table) that renders the actual control buttons
* in the <gr-context-controls> child component.
*/
export class GrContextControlsSection extends LitElement {
/** Must be of type GrDiffGroupType.CONTEXT_CONTROL. */
@property({type: Object})
group?: GrDiffGroup;
/**
* Semantic DOM diff testing does not work with just table fragments, so when
* running such tests the render() method has to wrap the DOM in a proper
* <table> element.
*/
@state() addTableWrapperForTesting = false;
@state() viewMode: DiffViewMode = DiffViewMode.SIDE_BY_SIDE;
@state() columns: ColumnsToShow = NO_COLUMNS;
@state() columnCount = 0;
@state() lineCountLeft = 0;
private readonly getDiffModel = resolve(this, diffModelToken);
constructor() {
super();
subscribe(
this,
() => this.getDiffModel().viewMode$,
viewMode => (this.viewMode = viewMode)
);
subscribe(
this,
() => this.getDiffModel().columnsToShow$,
columnsToShow => (this.columns = columnsToShow)
);
subscribe(
this,
() => this.getDiffModel().columnCount$,
columnCount => (this.columnCount = columnCount)
);
subscribe(
this,
() => this.getDiffModel().lineCountLeft$,
lineCountLeft => (this.lineCountLeft = lineCountLeft)
);
}
/**
* The browser API for handling selection does not (yet) work for selection
* across multiple shadow DOM elements. So we are rendering gr-diff components
* into the light DOM instead of the shadow DOM by overriding this method,
* which was the recommended workaround by the lit team.
* See also https://github.com/WICG/webcomponents/issues/79.
*
* Note that the <gr-context-controls> child component *does* use the shadow
* DOM, because the user has no intention to select the content of the context
* control buttons.
* TODO: That argument probably also applies to this component, so maybe
* reconsider making this a shadow DOM component!
*/
override createRenderRoot() {
return this;
}
private renderPaddingRow(whereClass: 'above' | 'below') {
if (!showAbove(this.group, this.lineCountLeft) && whereClass === 'above')
return;
if (!showBelow(this.group, this.lineCountLeft) && whereClass === 'below')
return;
const modeClass = this.isSideBySide() ? 'side-by-side' : 'unified';
const type = this.isSideBySide()
? GrDiffGroupType.CONTEXT_CONTROL
: undefined;
return html`
<tr
class=${['contextBackground', modeClass, whereClass].join(' ')}
left-type=${ifDefined(type)}
right-type=${ifDefined(type)}
>
${when(
this.columns.blame,
() => html`<td class="blame" data-line-number="0"></td>`
)}
${when(
this.columns.leftNumber,
() => html`<td class="contextLineNum"></td>`
)}
${when(this.columns.leftSign, () => html`<td class="sign"></td>`)}
${when(this.columns.leftContent, () => html`<td></td>`)}
${when(
this.columns.rightNumber,
() => html`<td class="contextLineNum"></td>`
)}
${when(this.columns.rightSign, () => html`<td class="sign"></td>`)}
${when(this.columns.rightContent, () => html`<td></td>`)}
</tr>
`;
}
private isSideBySide() {
return this.viewMode !== DiffViewMode.UNIFIED;
}
private createContextControlRow() {
// Span all columns, but not the blame column.
let colspan = this.columnCount;
if (this.columns.blame) colspan--;
const showConfig = getShowConfig(this.group, this.lineCountLeft);
return html`
<tr class=${['dividerRow', `show-${showConfig}`].join(' ')}>
${when(
this.columns.blame,
() => html`<td class="blame" data-line-number="0"></td>`
)}
<td class="dividerCell" colspan=${colspan}>
<gr-context-controls .group=${this.group}> </gr-context-controls>
</td>
</tr>
`;
}
override render() {
if (this.group?.type !== GrDiffGroupType.CONTEXT_CONTROL) return;
const rows = html`
${this.renderPaddingRow('above')} ${this.createContextControlRow()}
${this.renderPaddingRow('below')}
`;
if (this.addTableWrapperForTesting) {
return html`<table>
${rows}
</table>`;
}
return rows;
}
}
customElements.define('gr-context-controls-section', GrContextControlsSection);
declare global {
interface HTMLElementTagNameMap {
'gr-context-controls-section': GrContextControlsSection;
}
}