Convert files to typescript The change converts the following files to typescript: * elements/diff/gr-diff-cursor/gr-diff-cursor.ts Change-Id: Iedd1f6f02028dbed7afa4f6e7035c2d55ab410ee
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts index 99c8cc1..ebd410e 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts +++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts
@@ -15,106 +15,106 @@ * limitations under the License. */ -import '../../shared/gr-cursor-manager/gr-cursor-manager.js'; -import {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js'; -import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'; -import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js'; -import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; -import {PolymerElement} from '@polymer/polymer/polymer-element.js'; -import {htmlTemplate} from './gr-diff-cursor_html.js'; -import {ScrollMode} from '../../../constants/constants.js'; - -const DiffSides = { - LEFT: 'left', - RIGHT: 'right', -}; +import '../../shared/gr-cursor-manager/gr-cursor-manager'; +import {GrCursorManager} from '../../shared/gr-cursor-manager/gr-cursor-manager'; +import {afterNextRender} from '@polymer/polymer/lib/utils/render-status'; +import {dom} from '@polymer/polymer/lib/legacy/polymer.dom'; +import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners'; +import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin'; +import {PolymerElement} from '@polymer/polymer/polymer-element'; +import {htmlTemplate} from './gr-diff-cursor_html'; +import {ScrollMode} from '../../../constants/constants'; +import {customElement, property, observe} from '@polymer/decorators'; +import {DiffSide} from '../gr-diff/gr-diff-utils'; +import {GrDiffLineType} from '../gr-diff/gr-diff-line'; +import {PolymerSpliceChange} from '@polymer/polymer/interfaces'; +import {PolymerDomWrapper} from '../../../types/types'; +import {GrDiffGroupType} from '../gr-diff/gr-diff-group'; const DiffViewMode = { SIDE_BY_SIDE: 'SIDE_BY_SIDE', UNIFIED: 'UNIFIED_DIFF', }; +type GrDiffRowType = GrDiffLineType | GrDiffGroupType; + const LEFT_SIDE_CLASS = 'target-side-left'; const RIGHT_SIDE_CLASS = 'target-side-right'; -/** @extends PolymerElement */ -class GrDiffCursor extends GestureEventListeners( - LegacyElementMixin(PolymerElement)) { - static get template() { return htmlTemplate; } +// TODO(TS): Use proper GrDiff type once that file is converted to TS. +interface GrDiff extends HTMLElement { + path: string; + addDraftAtLine: (element: HTMLElement) => void; + createRangeComment: () => void; + getCursorStops: () => HTMLElement[]; + isRangeSelected: () => boolean; +} - static get is() { return 'gr-diff-cursor'; } +export interface GrDiffCursor { + $: { + cursorManager: GrCursorManager; + }; +} - static get properties() { - return { - /** - * Either DiffSides.LEFT or DiffSides.RIGHT. - */ - side: { - type: String, - value: DiffSides.RIGHT, - }, - /** @type {!HTMLElement|undefined} */ - diffRow: { - type: Object, - notify: true, - observer: '_rowChanged', - }, - - /** - * The diff views to cursor through and listen to. - */ - diffs: { - type: Array, - value() { return []; }, - }, - - /** - * If set, the cursor will attempt to move to the line number (instead of - * the first chunk) the next time the diff renders. It is set back to null - * when used. It should be only used if you want the line to be focused - * after initialization of the component and page should scroll - * to that position. This parameter should be set at most for one gr-diff - * element in the page. - * - * @type {?number} - */ - initialLineNumber: { - type: Number, - value: null, - }, - - /** - * The scroll behavior for the cursor. Values are 'never' and - * 'keep-visible'. 'keep-visible' will only scroll if the cursor is beyond - * the viewport. - */ - _scrollMode: { - type: String, - value: ScrollMode.KEEP_VISIBLE, - }, - - _focusOnMove: { - type: Boolean, - value: true, - }, - - _listeningForScroll: Boolean, - }; +@customElement('gr-diff-cursor') +export class GrDiffCursor extends GestureEventListeners( + LegacyElementMixin(PolymerElement) +) { + static get template() { + return htmlTemplate; } - static get observers() { - return [ - '_updateSideClass(side)', - '_diffsChanged(diffs.splices)', - ]; - } + private _boundHandleWindowScroll: () => void; + + private _boundHandleDiffRenderStart: () => void; + + private _boundHandleDiffRenderContent: () => void; + + private _boundHandleDiffLineSelected: (e: Event) => void; + + private _preventAutoScrollOnManualScroll = false; + + @property({type: String}) + side = DiffSide.RIGHT; + + @property({type: Object, notify: true, observer: '_rowChanged'}) + diffRow?: HTMLElement; + + @property({type: Object}) + diffs: GrDiff[] = []; + + /** + * If set, the cursor will attempt to move to the line number (instead of + * the first chunk) the next time the diff renders. It is set back to null + * when used. It should be only used if you want the line to be focused + * after initialization of the component and page should scroll + * to that position. This parameter should be set at most for one gr-diff + * element in the page. + */ + @property({type: Number}) + initialLineNumber: number | null = null; + + /** + * The scroll behavior for the cursor. Values are 'never' and + * 'keep-visible'. 'keep-visible' will only scroll if the cursor is beyond + * the viewport. + */ + @property({type: String}) + _scrollMode = ScrollMode.KEEP_VISIBLE; + + @property({type: Boolean}) + _focusOnMove = true; + + @property({type: Boolean}) + _listeningForScroll = false; constructor() { super(); this._boundHandleWindowScroll = () => this._handleWindowScroll(); this._boundHandleDiffRenderStart = () => this._handleDiffRenderStart(); this._boundHandleDiffRenderContent = () => this._handleDiffRenderContent(); - this._boundHandleDiffLineSelected = e => this._handleDiffLineSelected(e); + this._boundHandleDiffLineSelected = (e: Event) => + this._handleDiffLineSelected(e); } /** @override */ @@ -131,9 +131,12 @@ element with an actual lifecycle. This will be triggered only once per element. */ - this.dispatchEvent(new CustomEvent('ready', { - composed: true, bubbles: false, - })); + this.dispatchEvent( + new CustomEvent('ready', { + composed: true, + bubbles: false, + }) + ); }); } @@ -151,14 +154,14 @@ } moveLeft() { - this.side = DiffSides.LEFT; + this.side = DiffSide.LEFT; if (this._isTargetBlank()) { this.moveUp(); } } moveRight() { - this.side = DiffSides.RIGHT; + this.side = DiffSide.RIGHT; if (this._isTargetBlank()) { this.moveUp(); } @@ -182,17 +185,19 @@ moveToVisibleArea() { if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) { - this.$.cursorManager.moveToVisibleArea( - this._rowHasSide.bind(this)); + this.$.cursorManager.moveToVisibleArea(this._rowHasSide.bind(this)); } else { this.$.cursorManager.moveToVisibleArea(); } } - moveToNextChunk(opt_clipToTop, opt_navigateToNextFile) { - this.$.cursorManager.next(this._isFirstRowOfChunk.bind(this), - target => target.parentNode.scrollHeight, opt_clipToTop, - opt_navigateToNextFile); + moveToNextChunk(clipToTop?: boolean, navigateToNextFile?: boolean) { + this.$.cursorManager.next( + this._isFirstRowOfChunk.bind(this), + target => (target?.parentNode as HTMLElement)?.scrollHeight || 0, + clipToTop, + navigateToNextFile + ); this._fixSide(); } @@ -211,13 +216,8 @@ this._fixSide(); } - /** - * @param {number} number - * @param {string} side - * @param {string=} opt_path - */ - moveToLineNumber(number, side, opt_path) { - const row = this._findRowByNumberAndFile(number, side, opt_path); + moveToLineNumber(number: number, side: DiffSide, path?: string) { + const row = this._findRowByNumberAndFile(number, side, path); if (row) { this.side = side; this.$.cursorManager.setCursor(row); @@ -226,31 +226,27 @@ /** * Get the line number element targeted by the cursor row and side. - * - * @return {?Element|undefined} */ - getTargetLineElement() { + getTargetLineElement(): HTMLElement | null { let lineElSelector = '.lineNum'; if (!this.diffRow) { - return; + return null; } if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) { - lineElSelector += this.side === DiffSides.LEFT ? '.left' : '.right'; + lineElSelector += this.side === DiffSide.LEFT ? '.left' : '.right'; } return this.diffRow.querySelector(lineElSelector); } - getTargetDiffElement() { + getTargetDiffElement(): GrDiff | null { if (!this.diffRow) return null; - const hostOwner = dom( (this.diffRow)) - .getOwnerRoot(); - if (hostOwner && hostOwner.host && - hostOwner.host.tagName === 'GR-DIFF') { - return hostOwner.host; + const hostOwner = (dom(this.diffRow) as PolymerDomWrapper).getOwnerRoot(); + if (hostOwner?.host?.tagName === 'GR-DIFF') { + return hostOwner.host as GrDiff; } return null; } @@ -277,9 +273,9 @@ reInitCursor() { if (!this.diffRow) { // does not scroll during init unless requested - this._scrollMode = this.initialLineNumber ? - ScrollMode.KEEP_VISIBLE : - ScrollMode.NEVER; + this._scrollMode = this.initialLineNumber + ? ScrollMode.KEEP_VISIBLE + : ScrollMode.NEVER; if (this.initialLineNumber) { this.moveToLineNumber(this.initialLineNumber, this.side); this.initialLineNumber = null; @@ -323,20 +319,26 @@ this._preventAutoScrollOnManualScroll = false; } - _handleDiffLineSelected(event) { + _handleDiffLineSelected(event: Event) { + const customEvent = event as CustomEvent; this.moveToLineNumber( - event.detail.number, event.detail.side, event.detail.path); + customEvent.detail.number, + customEvent.detail.side, + customEvent.detail.path + ); } createCommentInPlace() { - const diffWithRangeSelected = this.diffs - .find(diff => diff.isRangeSelected()); + const diffWithRangeSelected = this.diffs.find(diff => + diff.isRangeSelected() + ); if (diffWithRangeSelected) { diffWithRangeSelected.createRangeComment(); } else { const line = this.getTargetLineElement(); - if (line) { - this.getTargetDiffElement().addDraftAtLine(line); + const diff = this.getTargetDiffElement(); + if (diff && line) { + diff.addDraftAtLine(line); } } } @@ -347,10 +349,12 @@ * {leftSide: true, number: 321} for line 321 of the base patch. * Returns null if an address is not available. * - * @return {?Object} + * @return */ getAddress() { - if (!this.diffRow) { return null; } + if (!this.diffRow) { + return null; + } // Get the line-number cell targeted by the cursor. If the mode is unified // then prefer the revision cell if available. @@ -363,10 +367,14 @@ } else { cell = this.diffRow.querySelector('.lineNum.' + this.side); } - if (!cell) { return null; } + if (!cell) { + return null; + } const number = cell.getAttribute('data-value'); - if (!number || number === 'FILE') { return null; } + if (!number || number === 'FILE') { + return null; + } return { leftSide: cell.matches('.left'), @@ -386,20 +394,22 @@ } } - _rowHasSide(row) { - const selector = (this.side === DiffSides.LEFT ? '.left' : '.right') + - ' + .content'; + _rowHasSide(row: Element) { + const selector = + (this.side === DiffSide.LEFT ? '.left' : '.right') + ' + .content'; return !!row.querySelector(selector); } - _isFirstRowOfChunk(row) { - const parentClassList = row.parentNode.classList; - return parentClassList.contains('section') && - parentClassList.contains('delta') && - !row.previousSibling; + _isFirstRowOfChunk(row: HTMLElement) { + const parentClassList = (row.parentNode as HTMLElement).classList; + return ( + parentClassList.contains('section') && + parentClassList.contains('delta') && + !row.previousSibling + ); } - _rowHasThread(row) { + _rowHasThread(row: HTMLElement) { return row.querySelector('.thread-group'); } @@ -408,10 +418,11 @@ * switch to the alternate side. */ _fixSide() { - if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE && - this._isTargetBlank()) { - this.side = this.side === DiffSides.LEFT ? - DiffSides.RIGHT : DiffSides.LEFT; + if ( + this._getViewMode() === DiffViewMode.SIDE_BY_SIDE && + this._isTargetBlank() + ) { + this.side = this.side === DiffSide.LEFT ? DiffSide.RIGHT : DiffSide.LEFT; } } @@ -421,45 +432,60 @@ } const actions = this._getActionsForRow(); - return (this.side === DiffSides.LEFT && !actions.left) || - (this.side === DiffSides.RIGHT && !actions.right); + return ( + (this.side === DiffSide.LEFT && !actions.left) || + (this.side === DiffSide.RIGHT && !actions.right) + ); } - _rowChanged(newRow, oldRow) { + _rowChanged(_: HTMLElement, oldRow: HTMLElement) { if (oldRow) { oldRow.classList.remove(LEFT_SIDE_CLASS, RIGHT_SIDE_CLASS); } this._updateSideClass(); } + @observe('side') _updateSideClass() { if (!this.diffRow) { return; } - this.toggleClass(LEFT_SIDE_CLASS, this.side === DiffSides.LEFT, - this.diffRow); - this.toggleClass(RIGHT_SIDE_CLASS, this.side === DiffSides.RIGHT, - this.diffRow); + this.toggleClass( + LEFT_SIDE_CLASS, + this.side === DiffSide.LEFT, + this.diffRow + ); + this.toggleClass( + RIGHT_SIDE_CLASS, + this.side === DiffSide.RIGHT, + this.diffRow + ); } - _isActionType(type) { - return type !== 'blank' && type !== 'contextControl'; + _isActionType(type: GrDiffRowType) { + return ( + type !== GrDiffLineType.BLANK && type !== GrDiffGroupType.CONTEXT_CONTROL + ); } _getActionsForRow() { const actions = {left: false, right: false}; if (this.diffRow) { actions.left = this._isActionType( - this.diffRow.getAttribute('left-type')); + this.diffRow.getAttribute('left-type') as GrDiffRowType + ); actions.right = this._isActionType( - this.diffRow.getAttribute('right-type')); + this.diffRow.getAttribute('right-type') as GrDiffRowType + ); } return actions; } _getStops() { return this.diffs.reduce( - (stops, diff) => stops.concat(diff.getCursorStops()), []); + (stops: HTMLElement[], diff) => stops.concat(diff.getCursorStops()), + [] + ); } _updateStops() { @@ -469,58 +495,83 @@ /** * Setup and tear down on-render listeners for any diffs that are added or * removed from the cursor. - * - * @private */ - _diffsChanged(changeRecord) { - if (!changeRecord) { return; } + @observe('diffs.splices') + _diffsChanged(changeRecord: PolymerSpliceChange<GrDiff[]>) { + if (!changeRecord) { + return; + } this._updateStops(); let splice; let i; - for (let spliceIdx = 0; - changeRecord.indexSplices && - spliceIdx < changeRecord.indexSplices.length; - spliceIdx++) { + for ( + let spliceIdx = 0; + changeRecord.indexSplices && spliceIdx < changeRecord.indexSplices.length; + spliceIdx++ + ) { splice = changeRecord.indexSplices[spliceIdx]; + // Removals must come before additions, because the gr-diff instances + // might be the same. + for (i = 0; i < splice?.removed.length; i++) { + splice.removed[i].removeEventListener( + 'render-start', + this._boundHandleDiffRenderStart + ); + splice.removed[i].removeEventListener( + 'render-content', + this._boundHandleDiffRenderContent + ); + splice.removed[i].removeEventListener( + 'line-selected', + this._boundHandleDiffLineSelected + ); + } + for (i = splice.index; i < splice.index + splice.addedCount; i++) { this.diffs[i].addEventListener( - 'render-start', this._boundHandleDiffRenderStart); + 'render-start', + this._boundHandleDiffRenderStart + ); this.diffs[i].addEventListener( - 'render-content', this._boundHandleDiffRenderContent); + 'render-content', + this._boundHandleDiffRenderContent + ); this.diffs[i].addEventListener( - 'line-selected', this._boundHandleDiffLineSelected); - } - - for (i = 0; i < splice.removed && splice.removed.length; i++) { - splice.removed[i].removeEventListener( - 'render-start', this._boundHandleDiffRenderStart); - splice.removed[i].removeEventListener( - 'render-content', this._boundHandleDiffRenderContent); - splice.removed[i].removeEventListener( - 'line-selected', this._boundHandleDiffLineSelected); + 'line-selected', + this._boundHandleDiffLineSelected + ); } } } - _findRowByNumberAndFile(targetNumber, side, opt_path) { + _findRowByNumberAndFile( + targetNumber: number, + side: DiffSide, + path?: string + ): HTMLElement | undefined { let stops; - if (opt_path) { - const diff = this.diffs.filter(diff => diff.path === opt_path)[0]; + if (path) { + const diff = this.diffs.filter(diff => diff.path === path)[0]; stops = diff.getCursorStops(); } else { stops = this.$.cursorManager.stops; } let selector; for (let i = 0; i < stops.length; i++) { - selector = '.lineNum.' + side + '[data-value="' + targetNumber + '"]'; + selector = `.lineNum.${side}[data-value="${targetNumber}"]`; if (stops[i].querySelector(selector)) { return stops[i]; } } + return undefined; } } -customElements.define(GrDiffCursor.is, GrDiffCursor); +declare global { + interface HTMLElementTagNameMap { + 'gr-diff-cursor': GrDiffCursor; + } +}
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.ts b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.ts index 9cffcef..a880320 100644 --- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.ts +++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.ts
@@ -109,7 +109,7 @@ next( condition?: Function, - getTargetHeight?: Function, + getTargetHeight?: (target: HTMLElement) => number, clipToTop?: boolean, navigateToNextFile?: boolean ) { @@ -272,7 +272,7 @@ _moveCursor( delta: number, condition?: Function, - getTargetHeight?: Function, + getTargetHeight?: (target: HTMLElement) => number, clipToTop?: boolean, navigateToNextFile?: boolean ) {
diff --git a/polygerrit-ui/app/types/types.ts b/polygerrit-ui/app/types/types.ts index 909f719..69a550b 100644 --- a/polygerrit-ui/app/types/types.ts +++ b/polygerrit-ui/app/types/types.ts
@@ -34,3 +34,15 @@ */ NOT_INSTRUMENTED = 'NOT_INSTRUMENTED', } + +/** + * If Polymer would have exported DomApiNative from its dom.js utility, then we + * would probably not need this type. We just use it for casting the return + * value of dom(element). + */ +export interface PolymerDomWrapper { + getOwnerRoot(): Node & OwnerRoot; +} +export interface OwnerRoot { + host?: HTMLElement; +}