Convert files to typescript * gr-annotation * gr-cursor-manager * scripts/util Change-Id: I95a1a3f5b7feb6204f25e8ebeba4dcea574a0fa9
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.ts b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.ts index 3d837bf..e50bcd7 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.ts +++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.ts
@@ -1,4 +1,3 @@ - /** * @license * Copyright (C) 2016 The Android Open Source Project @@ -15,8 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'; -import {sanitizeDOMValue} from '@polymer/polymer/lib/utils/settings.js'; +import {getSanitizeDOMValue} from '@polymer/polymer/lib/utils/settings'; // TODO(wyatta): refactor this to be <MARK> rather than <HL>. const ANNOTATION_TAG = 'HL'; @@ -25,19 +23,16 @@ const REGEX_ASTRAL_SYMBOL = /[\uD800-\uDBFF][\uDC00-\uDFFF]/; export const GrAnnotation = { - /** * The DOM API textContent.length calculation is broken when the text * contains Unicode. See https://mathiasbynens.be/notes/javascript-unicode . * - * @param {!Text} node text node. - * @return {number} The length of the text. */ - getLength(node) { - return this.getStringLength(node.textContent); + getLength(node: Node) { + return this.getStringLength(node.textContent || ''); }, - getStringLength(str) { + getStringLength(str: string) { return str.replace(REGEX_ASTRAL_SYMBOL, '_').length; }, @@ -45,26 +40,34 @@ * Annotates the [offset, offset+length) text segment in the parent with the * element definition provided as arguments. * - * @param {!Element} parent the node whose contents will be annotated. - * @param {number} offset the 0-based offset from which the annotation will - * start. - * @param {number} length of the annotated text. - * @param {GrAnnotation.ElementSpec} elementSpec the spec to create the - * annotating element. + * @param parent the node whose contents will be annotated. + * If parent is Text then parent.parentNode must not be null + * @param offset the 0-based offset from which the annotation will + * start. + * @param length of the annotated text. + * @param elementSpec the spec to create the + * annotating element. */ - annotateWithElement(parent, offset, length, {tagName, attributes = {}}) { - let childNodes; + annotateWithElement( + parent: Node, + offset: number, + length: number, + elSpec: ElementSpec + ) { + const tagName = elSpec.tagName; + const attributes = elSpec.attributes || {}; + let childNodes: Node[]; if (parent instanceof Element) { childNodes = Array.from(parent.childNodes); } else if (parent instanceof Text) { childNodes = [parent]; - parent = parent.parentNode; + parent = parent.parentNode!; } else { return; } - const nestedNodes = []; + const nestedNodes: Node[] = []; for (let node of childNodes) { const initialNodeLength = this.getLength(node); // If the current node is completely before the offset. @@ -87,12 +90,12 @@ } const wrapper = document.createElement(tagName); - const sanitizer = sanitizeDOMValue; + const sanitizer = getSanitizeDOMValue(); for (const [name, value] of Object.entries(attributes)) { wrapper.setAttribute( - name, sanitizer ? - sanitizer(value, name, 'attribute', wrapper) : - value); + name, + sanitizer ? sanitizer(value, name, 'attribute', wrapper) : value + ); } for (const inner of nestedNodes) { parent.replaceChild(wrapper, inner); @@ -105,8 +108,13 @@ * element. If the element has child elements, the range is split and * applied as deeply as possible. */ - annotateElement(parent, offset, length, cssClass) { - const nodes = [].slice.apply(parent.childNodes); + annotateElement( + parent: HTMLElement, + offset: number, + length: number, + cssClass: string + ) { + const nodes: Array<HTMLElement | Text> = [].slice.apply(parent.childNodes); let nodeLength; let subLength; @@ -141,41 +149,40 @@ /** * Wraps node in annotation tag with cssClass, replacing the node in DOM. - * - * @return {!Element} Wrapped node. */ - wrapInHighlight(node, cssClass) { + wrapInHighlight(node: Element | Text, cssClass: string) { let hl; - if (node.tagName === ANNOTATION_TAG) { + if (!(node instanceof Text) && node.tagName === ANNOTATION_TAG) { hl = node; hl.classList.add(cssClass); } else { hl = document.createElement(ANNOTATION_TAG); hl.className = cssClass; - dom(node.parentElement).replaceChild(hl, node); - dom(hl).appendChild(node); + if (node.parentElement) node.parentElement.replaceChild(hl, node); + hl.appendChild(node); } return hl; }, /** * Splits Text Node and wraps it in hl with cssClass. - * Wraps trailing part after split, tailing one if opt_firstPart is true. - * - * @param {!Node} node - * @param {number} offset - * @param {string} cssClass - * @param {boolean=} opt_firstPart + * Wraps trailing part after split, tailing one if firstPart is true. */ - splitAndWrapInHighlight(node, offset, cssClass, opt_firstPart) { + splitAndWrapInHighlight( + node: Text, + offset: number, + cssClass: string, + firstPart?: boolean + ) { if (this.getLength(node) === offset || offset === 0) { return this.wrapInHighlight(node, cssClass); } else { - if (opt_firstPart) { + if (firstPart) { this.splitNode(node, offset); // Node points to first part of the Text, second one is sibling. } else { - node = this.splitNode(node, offset); + // if node is Text then splitNode will return a Text + node = this.splitNode(node, offset) as Text; } return this.wrapInHighlight(node, cssClass); } @@ -184,29 +191,28 @@ /** * Splits Node at offset. * If Node is Element, it's cloned and the node at offset is split too. - * - * @param {!Node} node - * @param {number} offset - * @return {!Node} Trailing Node. */ - splitNode(element, offset) { + splitNode(element: Node, offset: number) { if (element instanceof Text) { return this.splitTextNode(element, offset); } const tail = element.cloneNode(false); - element.parentElement.insertBefore(tail, element.nextSibling); + + if (element.parentElement) + element.parentElement.insertBefore(tail, element.nextSibling); // Skip nodes before offset. let node = element.firstChild; - while (node && - this.getLength(node) <= offset || - this.getLength(node) === 0) { + while ( + node && + (this.getLength(node) <= offset || this.getLength(node) === 0) + ) { offset -= this.getLength(node); node = node.nextSibling; } - if (this.getLength(node) > offset) { + if (node && this.getLength(node) > offset) { tail.appendChild(this.splitNode(node, offset)); } - while (node.nextSibling) { + while (node && node.nextSibling) { tail.appendChild(node.nextSibling); } return tail; @@ -218,12 +224,10 @@ * DOM Api for splitText() is broken for Unicode: * https://mathiasbynens.be/notes/javascript-unicode * - * @param {!Text} node - * @param {number} offset - * @return {!Text} Trailing Text Node. + * @return Trailing Text Node. */ - splitTextNode(node, offset) { - if (node.textContent.match(REGEX_ASTRAL_SYMBOL)) { + splitTextNode(node: Text, offset: number) { + if (node.textContent && node.textContent.match(REGEX_ASTRAL_SYMBOL)) { // TODO (viktard): Polyfill Array.from for IE10. const head = Array.from(node.textContent); const tail = head.splice(offset); @@ -242,7 +246,7 @@ } }, - _annotateText(node, offset, length, cssClass) { + _annotateText(node: Text, offset: number, length: number, cssClass: string) { const nodeLength = this.getLength(node); // There are four cases: @@ -262,8 +266,12 @@ this.splitAndWrapInHighlight(node, offset, cssClass, false); } else { // Case 4 - this.splitAndWrapInHighlight(this.splitTextNode(node, offset), length, - cssClass, true); + this.splitAndWrapInHighlight( + this.splitTextNode(node, offset), + length, + cssClass, + true + ); } }, }; @@ -271,9 +279,8 @@ /** * Data used to construct an element. * - * @typedef {{ - * tagName: string, - * attributes: (!Object<string, *>|undefined) - * }} */ -GrAnnotation.ElementSpec; +export interface ElementSpec { + tagName: string; + attributes?: {[attributeName: string]: string | undefined}; +}
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 de9dcc2..ffcd10c 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
@@ -14,97 +14,85 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -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-cursor-manager_html.js'; -import {ScrollMode} from '../../../constants/constants.js'; +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-cursor-manager_html'; +import {ScrollMode} from '../../../constants/constants'; +import {customElement, property, observe} from '@polymer/decorators'; + +export interface GrCursorManager { + $: {}; +} + +declare global { + interface HTMLElementTagNameMap { + 'gr-cursor-manager': GrCursorManager; + } +} // Time in which pressing n key again after the toast navigates to next file const NAVIGATE_TO_NEXT_FILE_TIMEOUT_MS = 5000; -/** @extends PolymerElement */ -class GrCursorManager extends GestureEventListeners( - LegacyElementMixin( - PolymerElement)) { - static get template() { return htmlTemplate; } - - static get is() { return 'gr-cursor-manager'; } - - static get properties() { - return { - stops: { - type: Array, - value() { - return []; - }, - observer: '_updateIndex', - }, - /** - * @type {?Object} - */ - target: { - type: Object, - notify: true, - observer: '_scrollToTarget', - }, - /** - * The height of content intended to be included with the target. - * - * @type {?number} - */ - _targetHeight: Number, - - /** - * The index of the current target (if any). -1 otherwise. - */ - index: { - type: Number, - value: -1, - notify: true, - }, - - /** - * The class to apply to the current target. Use null for no class. - */ - cursorTargetClass: { - type: String, - 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. - * TODO (beckysiegel) figure out why it can be undefined - * - * @type {string|undefined} - */ - scrollMode: { - type: String, - value: ScrollMode.NEVER, - }, - - /** - * When true, will call element.focus() during scrolling. - */ - focusOnMove: { - type: Boolean, - value: false, - }, - - /** - * The scrollTopMargin defines height of invisible area at the top - * of the page. If cursor locates inside this margin - it is - * not visible, because it is covered by some other element. - */ - scrollTopMargin: { - type: Number, - value: 0, - }, - }; +@customElement('gr-cursor-manager') +export class GrCursorManager extends GestureEventListeners( + LegacyElementMixin(PolymerElement) +) { + static get template() { + return htmlTemplate; } + @property({type: Object, notify: true}) + target: HTMLElement | null = null; + + /** + * The height of content intended to be included with the target. + */ + @property({type: Number}) + _targetHeight: number | null = null; + + /** + * The index of the current target (if any). -1 otherwise. + */ + @property({type: Number, notify: true}) + index = -1; + + /** + * The class to apply to the current target. Use null for no class. + */ + @property({type: String}) + cursorTargetClass: string | 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. + * TODO (beckysiegel) figure out why it can be undefined + * + * @type {string|undefined} + */ + @property({type: String}) + scrollMode: string = ScrollMode.NEVER; + + /** + * When true, will call element.focus() during scrolling. + */ + @property({type: Boolean}) + focusOnMove = false; + + /** + * The scrollTopMargin defines height of invisible area at the top + * of the page. If cursor locates inside this margin - it is + * not visible, because it is covered by some other element. + */ + @property({type: Number}) + scrollTopMargin = 0; + + private _lastDisplayedNavigateToNextFileToast: number | null = null; + + @property({type: Array}) + stops: HTMLElement[] = []; + /** @override */ detached() { super.detached(); @@ -114,27 +102,36 @@ /** * Move the cursor forward. Clipped to the ends of the stop list. * - * @param {!Function=} opt_condition Optional stop condition. If a condition + * @param condition Optional stop condition. If a condition * is passed the cursor will continue to move in the specified direction * until the condition is met. - * @param {!Function=} opt_getTargetHeight Optional function to calculate the + * @param getTargetHeight Optional function to calculate the * height of the target's 'section'. The height of the target itself is * sometimes different, used by the diff cursor. - * @param {boolean=} opt_clipToTop When none of the next indices match, move + * @param clipToTop When none of the next indices match, move * back to first instead of to last. - * @param {boolean=} opt_navigateToNextFile Navigate to next unreviewed file + * @param navigateToNextFile Navigate to next unreviewed file * if user presses next on the last diff chunk * @private */ - next(opt_condition, opt_getTargetHeight, opt_clipToTop, - opt_navigateToNextFile) { - this._moveCursor(1, opt_condition, opt_getTargetHeight, opt_clipToTop, - opt_navigateToNextFile); + next( + condition?: Function, + getTargetHeight?: Function, + clipToTop?: boolean, + navigateToNextFile?: boolean + ) { + this._moveCursor( + 1, + condition, + getTargetHeight, + clipToTop, + navigateToNextFile + ); } - previous(opt_condition) { - this._moveCursor(-1, opt_condition); + previous(condition?: Function) { + this._moveCursor(-1, condition); } /** @@ -143,21 +140,21 @@ * The method uses IntersectionObservers API. If browser * doesn't support this API the method does nothing * - * @param {!Function=} opt_condition Optional condition. If a condition - * is passed only stops which meet conditions are taken into account. + * @param condition Optional condition. If a condition + * is passed only stops which meet conditions are taken into account. */ - moveToVisibleArea(opt_condition) { + moveToVisibleArea(condition?: (el: Element) => boolean) { if (!this.stops || !this._isIntersectionObserverSupported()) { return; } - const filteredStops = opt_condition ? this.stops.filter(opt_condition) - : this.stops; + const filteredStops = condition ? this.stops.filter(condition) : this.stops; const dims = this._getWindowDims(); - const windowCenter = - Math.round((dims.innerHeight + this.scrollTopMargin) / 2); + const windowCenter = Math.round( + (dims.innerHeight + this.scrollTopMargin) / 2 + ); - let closestToTheCenter = null; - let minDistanceToCenter = null; + let closestToTheCenter: HTMLElement | null = null; + let minDistanceToCenter: number | null = null; let unobservedCount = filteredStops.length; const observer = new IntersectionObserver(entries => { @@ -170,21 +167,26 @@ // In Edge it is recommended to use intersectionRatio instead of // isIntersecting. const isInsideViewport = - entry.isIntersecting || entry.intersectionRatio > 0; + entry.isIntersecting || entry.intersectionRatio > 0; if (!isInsideViewport) { return; } - const center = entry.boundingClientRect.top + Math.round( - entry.boundingClientRect.height / 2); + const center = + entry.boundingClientRect.top + + Math.round(entry.boundingClientRect.height / 2); const distanceToWindowCenter = Math.abs(center - windowCenter); - if (minDistanceToCenter === null || - distanceToWindowCenter < minDistanceToCenter) { - closestToTheCenter = entry.target; + if ( + minDistanceToCenter === null || + distanceToWindowCenter < minDistanceToCenter + ) { + // entry.target comes from the filteredStops array, + // hence it is an HTMLElement + closestToTheCenter = entry.target as HTMLElement; minDistanceToCenter = distanceToWindowCenter; } }); unobservedCount -= entries.length; - if (unobservedCount == 0 && closestToTheCenter) { + if (unobservedCount === 0 && closestToTheCenter) { // set cursor when all stops were observed. // In most cases the target is visible, so scroll is not // needed. But in rare cases the target can become invisible @@ -209,13 +211,12 @@ /** * Set the cursor to an arbitrary element. * - * @param {!HTMLElement} element - * @param {boolean=} opt_noScroll prevent any potential scrolling in response - * setting the cursor. + * @param noScroll prevent any potential scrolling in response + * setting the cursor. */ - setCursor(element, opt_noScroll) { + setCursor(element: HTMLElement, noScroll?: boolean) { let behavior; - if (opt_noScroll) { + if (noScroll) { behavior = this.scrollMode; this.scrollMode = ScrollMode.NEVER; } @@ -225,7 +226,9 @@ this._updateIndex(); this._decorateTarget(); - if (opt_noScroll) { this.scrollMode = behavior; } + if (noScroll && behavior) { + this.scrollMode = behavior; + } } unsetCursor() { @@ -255,29 +258,34 @@ } } - setCursorAtIndex(index, opt_noScroll) { - this.setCursor(this.stops[index], opt_noScroll); + setCursorAtIndex(index: number, noScroll?: boolean) { + this.setCursor(this.stops[index], noScroll); } /** * Move the cursor forward or backward by delta. Clipped to the beginning or * end of stop list. * - * @param {number} delta either -1 or 1. - * @param {!Function=} opt_condition Optional stop condition. If a condition - * is passed the cursor will continue to move in the specified direction - * until the condition is met. - * @param {!Function=} opt_getTargetHeight Optional function to calculate the - * height of the target's 'section'. The height of the target itself is - * sometimes different, used by the diff cursor. - * @param {boolean=} opt_clipToTop When none of the next indices match, move - * back to first instead of to last. - * @param {boolean=} opt_navigateToNextFile Navigate to next unreviewed file - * if user presses next on the last diff chunk + * @param delta either -1 or 1. + * @param condition Optional stop condition. If a condition + * is passed the cursor will continue to move in the specified direction + * until the condition is met. + * @param getTargetHeight Optional function to calculate the + * height of the target's 'section'. The height of the target itself is + * sometimes different, used by the diff cursor. + * @param clipToTop When none of the next indices match, move + * back to first instead of to last. + * @param navigateToNextFile Navigate to next unreviewed file + * if user presses next on the last diff chunk * @private */ - _moveCursor(delta, opt_condition, opt_getTargetHeight, opt_clipToTop, - opt_navigateToNextFile) { + _moveCursor( + delta: number, + condition?: Function, + getTargetHeight?: Function, + clipToTop?: boolean, + navigateToNextFile?: boolean + ) { if (!this.stops.length) { this.unsetCursor(); return; @@ -285,7 +293,7 @@ this._unDecorateTarget(); - const newIndex = this._getNextindex(delta, opt_condition, opt_clipToTop); + const newIndex = this._getNextindex(delta, condition, clipToTop); let newTarget = null; if (newIndex !== -1) { @@ -297,42 +305,53 @@ * that pressing n again will navigate them to next unreviewed file. * If click happens within the time limit, then navigate to next file */ - if (opt_navigateToNextFile && this.index === newIndex) { + if (navigateToNextFile && this.index === newIndex) { if (newIndex === this.stops.length - 1) { - if (this._lastDisplayedNavigateToNextFileToast && (Date.now() - - this._lastDisplayedNavigateToNextFileToast <= - NAVIGATE_TO_NEXT_FILE_TIMEOUT_MS)) { + if ( + this._lastDisplayedNavigateToNextFileToast && + Date.now() - this._lastDisplayedNavigateToNextFileToast <= + NAVIGATE_TO_NEXT_FILE_TIMEOUT_MS + ) { // reset for next file this._lastDisplayedNavigateToNextFileToast = null; - this.dispatchEvent(new CustomEvent( - 'navigate-to-next-unreviewed-file', { - composed: true, bubbles: true, - })); + this.dispatchEvent( + new CustomEvent('navigate-to-next-unreviewed-file', { + composed: true, + bubbles: true, + }) + ); return; } this._lastDisplayedNavigateToNextFileToast = Date.now(); - this.dispatchEvent(new CustomEvent('show-alert', { - detail: { - message: 'Press n again to navigate to next unreviewed file', - }, - composed: true, bubbles: true, - })); + this.dispatchEvent( + new CustomEvent('show-alert', { + detail: { + message: 'Press n again to navigate to next unreviewed file', + }, + composed: true, + bubbles: true, + }) + ); return; } } this.index = newIndex; - this.target = newTarget; + this.target = newTarget as HTMLElement; - if (!this.target) { return; } + if (!newTarget) { + return; + } - if (opt_getTargetHeight) { - this._targetHeight = opt_getTargetHeight(newTarget); + if (getTargetHeight) { + this._targetHeight = getTargetHeight(newTarget); } else { this._targetHeight = newTarget.scrollHeight; } - if (this.focusOnMove) { this.target.focus(); } + if (this.focusOnMove) { + newTarget.focus(); + } this._decorateTarget(); } @@ -352,14 +371,14 @@ /** * Get the next stop index indicated by the delta direction. * - * @param {number} delta either -1 or 1. - * @param {!Function=} opt_condition Optional stop condition. - * @param {boolean=} opt_clipToTop When none of the next indices match, move - * back to first instead of to last. - * @return {number} the new index. + * @param delta either -1 or 1. + * @param condition Optional stop condition. + * @param clipToTop When none of the next indices match, move + * back to first instead of to last. + * @return the new index. * @private */ - _getNextindex(delta, opt_condition, opt_clipToTop) { + _getNextindex(delta: number, condition?: Function, clipToTop?: boolean) { if (!this.stops.length) { return -1; } @@ -371,15 +390,18 @@ } do { newIndex = newIndex + delta; - } while ((delta > 0 || newIndex > 0) && - (delta < 0 || newIndex < this.stops.length - 1) && - opt_condition && !opt_condition(this.stops[newIndex])); + } while ( + (delta > 0 || newIndex > 0) && + (delta < 0 || newIndex < this.stops.length - 1) && + condition && + !condition(this.stops[newIndex]) + ); newIndex = Math.max(0, Math.min(this.stops.length - 1, newIndex)); // If we failed to satisfy the condition: - if (opt_condition && !opt_condition(this.stops[newIndex])) { - if (delta < 0 || opt_clipToTop) { + if (condition && !condition(this.stops[newIndex])) { + if (delta < 0 || clipToTop) { return 0; } else if (delta > 0) { return this.stops.length - 1; @@ -390,6 +412,7 @@ return newIndex; } + @observe('stops') _updateIndex() { if (!this.target) { this.index = -1; @@ -407,35 +430,44 @@ /** * Calculate where the element is relative to the window. * - * @param {!Object} target Target to scroll to. - * @return {number} Distance to top of the target. + * @param target Target to scroll to. + * @return Distance to top of the target. */ - _getTop(target) { - let top = target.offsetTop; - for (let offsetParent = target.offsetParent; + _getTop(target: HTMLElement) { + let top: number = target.offsetTop; + for ( + let offsetParent = target.offsetParent; offsetParent; - offsetParent = offsetParent.offsetParent) { - top += offsetParent.offsetTop; + offsetParent = (offsetParent as HTMLElement).offsetParent + ) { + top += (offsetParent as HTMLElement).offsetTop; } return top; } /** - * @return {boolean} + * @return */ - _targetIsVisible(top) { + _targetIsVisible(top: number) { const dims = this._getWindowDims(); - return this.scrollMode === ScrollMode.KEEP_VISIBLE && - top > (dims.pageYOffset + this.scrollTopMargin) && - top < dims.pageYOffset + dims.innerHeight; + return ( + this.scrollMode === ScrollMode.KEEP_VISIBLE && + top > dims.pageYOffset + this.scrollTopMargin && + top < dims.pageYOffset + dims.innerHeight + ); } - _calculateScrollToValue(top, target) { + _calculateScrollToValue(top: number, target: HTMLElement) { const dims = this._getWindowDims(); - return top + this.scrollTopMargin - (dims.innerHeight / 3) + - (target.offsetHeight / 2); + return ( + top + + this.scrollTopMargin - + dims.innerHeight / 3 + + target.offsetHeight / 2 + ); } + @observe('target') _scrollToTarget() { if (!this.target || this.scrollMode === ScrollMode.NEVER) { return; @@ -443,8 +475,9 @@ const dims = this._getWindowDims(); const top = this._getTop(this.target); - const bottomIsVisible = this._targetHeight ? - this._targetIsVisible(top + this._targetHeight) : true; + const bottomIsVisible = this._targetHeight + ? this._targetIsVisible(top + this._targetHeight) + : true; const scrollToValue = this._calculateScrollToValue(top, this.target); if (this._targetIsVisible(top)) { @@ -473,5 +506,3 @@ }; } } - -customElements.define(GrCursorManager.is, GrCursorManager);
diff --git a/polygerrit-ui/app/scripts/util.ts b/polygerrit-ui/app/scripts/util.ts index e4be858..6e4464f 100644 --- a/polygerrit-ui/app/scripts/util.ts +++ b/polygerrit-ui/app/scripts/util.ts
@@ -15,10 +15,14 @@ * limitations under the License. */ +interface CancelablePromise<T> extends Promise<T> { + cancel(): void; +} + // TODO (dmfilippov): Each function must be exported separately. According to // the code style guide, a namespacing is not allowed. export const util = { - getCookie(name) { + getCookie(name: string) { const key = name + '='; const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { @@ -41,22 +45,28 @@ * {isCancelled: true} synchronously. If the inner promise for a cancelled * promise resolves or rejects this is ignored. */ - makeCancelable: promise => { + makeCancelable<T>(promise: Promise<T>) { // True if the promise is either resolved or reject (possibly cancelled) let isDone = false; - let rejectPromise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let rejectPromise: (reason?: any) => void; - const wrappedPromise = new Promise((resolve, reject) => { - rejectPromise = reject; - promise.then(val => { - if (!isDone) resolve(val); - isDone = true; - }, error => { - if (!isDone) reject(error); - isDone = true; - }); - }); + const wrappedPromise: CancelablePromise<T> = new Promise( + (resolve, reject) => { + rejectPromise = reject; + promise.then( + val => { + if (!isDone) resolve(val); + isDone = true; + }, + error => { + if (!isDone) reject(error); + isDone = true; + } + ); + } + ) as CancelablePromise<T>; wrappedPromise.cancel = () => { if (isDone) return;