From 77eaabfab2ffbf0411631ad9ccde1dcd8ab4b829 Mon Sep 17 00:00:00 2001 From: Oliver Eivak Date: Sat, 7 Apr 2018 22:32:11 +0300 Subject: [PATCH] apply patch ebondu#universal-support (edcarroll/ng2-semantic-ui/pull/270) --- .../components/sidebar/sidebar.component.ts | 2 - package-lock.json | 5 --- package.json | 1 - src/misc/util/helpers/closest.ts | 37 +++++++++++++++++++ src/modules/checkbox/components/checkbox.ts | 2 +- src/modules/checkbox/components/radio.ts | 2 +- .../datepicker/components/datepicker.ts | 2 +- .../directives/datepicker.directive.ts | 2 +- src/modules/datepicker/views/calendar-view.ts | 4 +- .../dropdown/directives/dropdown-menu.ts | 8 ++-- src/modules/dropdown/directives/dropdown.ts | 2 +- src/modules/modal/components/modal.ts | 28 +++++++------- src/modules/popup/classes/popup-controller.ts | 10 +++-- src/modules/popup/components/popup.ts | 4 +- src/modules/search/components/search.ts | 6 +-- src/modules/search/services/search.service.ts | 14 ++++--- src/modules/select/classes/select-base.ts | 8 ++-- src/modules/select/components/multi-select.ts | 2 +- .../select/directives/select-search.ts | 6 +-- .../sidebar/components/sidebar-sibling.ts | 2 +- .../sidebar/services/sidebar.service.ts | 4 +- .../classes/transition-controller.ts | 4 +- src/sui.module.ts | 2 + tslint.json | 3 +- 24 files changed, 101 insertions(+), 59 deletions(-) create mode 100644 src/misc/util/helpers/closest.ts diff --git a/demo/src/app/components/sidebar/sidebar.component.ts b/demo/src/app/components/sidebar/sidebar.component.ts index c242f4023..60117cb6b 100644 --- a/demo/src/app/components/sidebar/sidebar.component.ts +++ b/demo/src/app/components/sidebar/sidebar.component.ts @@ -1,6 +1,4 @@ import { Component, HostBinding, Output, EventEmitter, HostListener, isDevMode } from "@angular/core"; -// Polyfill for IE -import "element-closest"; interface IAugmentedElement extends Element { closest(selector:string):IAugmentedElement; diff --git a/package-lock.json b/package-lock.json index 96ce55b3b..1c25f7d37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2684,11 +2684,6 @@ "integrity": "sha1-16RpZAnKCZXidQFW2mEsIhr62E0=", "dev": true }, - "element-closest": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/element-closest/-/element-closest-2.0.2.tgz", - "integrity": "sha1-cqdAoQdFM4LijfnOXbtajfD5Zuw=" - }, "elliptic": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", diff --git a/package.json b/package.json index afdc92d39..3fb6fe0ae 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "dependencies": { "bowser": "^1.7.2", "date-fns": "2.0.0-alpha.1", - "element-closest": "^2.0.2", "extend": "^3.0.1", "is-ua-webview": "^1.0.0", "popper.js": "^1.14.0", diff --git a/src/misc/util/helpers/closest.ts b/src/misc/util/helpers/closest.ts new file mode 100644 index 000000000..88d9298e4 --- /dev/null +++ b/src/misc/util/helpers/closest.ts @@ -0,0 +1,37 @@ +// element-closest | CC0-1.0 | github.com/jonathantneal/closest + +/** @this {Foo} */ +if (typeof Element !== "undefined") { + if (typeof Element.prototype.matches !== "function") { + Element.prototype.matches = Element.prototype.msMatchesSelector || + (Element as any).prototype["mozMatchesSelector"] || + Element.prototype.webkitMatchesSelector || + function matches(selector:any):boolean { + const element = this; + const elements = (element.document || element.ownerDocument).querySelectorAll(selector); + let index = 0; + + while (elements[index] && elements[index] !== element) { + ++index; + } + + return Boolean(elements[index]); + }; + } + + if (typeof Element.prototype["closest"] !== "function") { + Element.prototype["closest"] = function closest(selector:any):any { + let element = this; + + while (element && element.nodeType === 1) { + if (element.matches(selector)) { + return element; + } + + element = element.parentNode; + } + + return undefined; + }; + } +} diff --git a/src/modules/checkbox/components/checkbox.ts b/src/modules/checkbox/components/checkbox.ts index c020e395c..39d35aee5 100644 --- a/src/modules/checkbox/components/checkbox.ts +++ b/src/modules/checkbox/components/checkbox.ts @@ -67,7 +67,7 @@ export class SuiCheckbox implements ICustomValueAccessorHost { } @HostListener("mousedown", ["$event"]) - public onMouseDown(e:MouseEvent):void { + public onMouseDown(e:any):void { e.preventDefault(); } diff --git a/src/modules/checkbox/components/radio.ts b/src/modules/checkbox/components/radio.ts index bd30fa3a3..9bc94aceb 100644 --- a/src/modules/checkbox/components/radio.ts +++ b/src/modules/checkbox/components/radio.ts @@ -77,7 +77,7 @@ export class SuiRadio implements ICustomValueAccessorHost { } @HostListener("mousedown", ["$event"]) - public onMouseDown(e:MouseEvent):void { + public onMouseDown(e:any):void { e.preventDefault(); } diff --git a/src/modules/datepicker/components/datepicker.ts b/src/modules/datepicker/components/datepicker.ts index 28a74d52a..920de6c60 100644 --- a/src/modules/datepicker/components/datepicker.ts +++ b/src/modules/datepicker/components/datepicker.ts @@ -45,7 +45,7 @@ export class SuiDatepicker { } @HostListener("mousedown", ["$event"]) - public onMouseDown(e:MouseEvent):void { + public onMouseDown(e:any):void { e.preventDefault(); } } diff --git a/src/modules/datepicker/directives/datepicker.directive.ts b/src/modules/datepicker/directives/datepicker.directive.ts index a78851614..d30df5727 100644 --- a/src/modules/datepicker/directives/datepicker.directive.ts +++ b/src/modules/datepicker/directives/datepicker.directive.ts @@ -189,7 +189,7 @@ export class SuiDatepickerDirective } @HostListener("keydown", ["$event"]) - public onKeyDown(e:KeyboardEvent):void { + public onKeyDown(e:any):void { if (e.keyCode === KeyCode.Escape) { this.close(); } diff --git a/src/modules/datepicker/views/calendar-view.ts b/src/modules/datepicker/views/calendar-view.ts index 7072e8216..c527c79e9 100644 --- a/src/modules/datepicker/views/calendar-view.ts +++ b/src/modules/datepicker/views/calendar-view.ts @@ -54,7 +54,7 @@ export abstract class CalendarView implements AfterViewInit, OnDestroy { this._type = viewType; this.ranges = ranges; - this._documentKeyDownListener = renderer.listen("document", "keydown", (e:KeyboardEvent) => this.onDocumentKeyDown(e)); + this._documentKeyDownListener = renderer.listen("document", "keydown", (e:any) => this.onDocumentKeyDown(e)); } // Template Methods @@ -111,7 +111,7 @@ export abstract class CalendarView implements AfterViewInit, OnDestroy { } } - private onDocumentKeyDown(e:KeyboardEvent):void { + private onDocumentKeyDown(e:any):void { if (this._highlightedItem && e.keyCode === KeyCode.Enter) { this.setDate(this._highlightedItem); return; diff --git a/src/modules/dropdown/directives/dropdown-menu.ts b/src/modules/dropdown/directives/dropdown-menu.ts index 7df10514f..6c0de4e57 100644 --- a/src/modules/dropdown/directives/dropdown-menu.ts +++ b/src/modules/dropdown/directives/dropdown-menu.ts @@ -6,7 +6,7 @@ import { Transition, SuiTransition, TransitionController, TransitionDirection } import { HandledEvent, IAugmentedElement, KeyCode } from "../../../misc/util/index"; import { DropdownService, DropdownAutoCloseType } from "../services/dropdown.service"; // Polyfill for IE -import "element-closest"; +import "../../../misc/util/helpers/closest"; @Directive({ // We must attach to every '.item' as Angular doesn't support > selectors. @@ -144,11 +144,11 @@ export class SuiDropdownMenu extends SuiTransition implements AfterContentInit, this.menuAutoSelectFirst = false; this.menuSelectedItemClass = "selected"; - this._documentKeyDownListener = renderer.listen("document", "keydown", (e:KeyboardEvent) => this.onDocumentKeyDown(e)); + this._documentKeyDownListener = renderer.listen("document", "keydown", (e:any) => this.onDocumentKeyDown(e)); } @HostListener("click", ["$event"]) - public onClick(e:HandledEvent & MouseEvent):void { + public onClick(e:HandledEvent & any):void { if (!e.eventHandled) { e.eventHandled = true; @@ -162,7 +162,7 @@ export class SuiDropdownMenu extends SuiTransition implements AfterContentInit, } } - public onDocumentKeyDown(e:KeyboardEvent):void { + public onDocumentKeyDown(e:any):void { // Only the root dropdown (i.e. not nested dropdowns) is responsible for keeping track of the currently selected item. if (this._service.isOpen && !this._service.isNested) { // Stop document events like scrolling while open. diff --git a/src/modules/dropdown/directives/dropdown.ts b/src/modules/dropdown/directives/dropdown.ts index 7916f182d..35156f2c9 100644 --- a/src/modules/dropdown/directives/dropdown.ts +++ b/src/modules/dropdown/directives/dropdown.ts @@ -124,7 +124,7 @@ export class SuiDropdown implements AfterContentInit { } @HostListener("keypress", ["$event"]) - public onKeypress(e:HandledEvent & KeyboardEvent):void { + public onKeypress(e:HandledEvent & any):void { // Block the keyboard event from being fired on parent dropdowns. if (!e.eventHandled) { diff --git a/src/modules/modal/components/modal.ts b/src/modules/modal/components/modal.ts index 55716e032..d8a4541cd 100644 --- a/src/modules/modal/components/modal.ts +++ b/src/modules/modal/components/modal.ts @@ -32,7 +32,7 @@ import { ModalConfig, ModalSize } from "../classes/modal-config"; #modal> - + @@ -270,27 +270,29 @@ export class SuiModal implements OnInit, AfterViewInit { // Decides whether the modal needs to reposition to allow scrolling. private updateScroll():void { - // Semantic UI modal margin is 3.5rem, which is relative to the global font size, so for compatibility: - const fontSize = parseFloat(window.getComputedStyle(document.documentElement).getPropertyValue("font-size")); - const margin = fontSize * 3.5; - - // _mustAlwaysScroll works by stopping _mustScroll from being automatically updated, so it stays `true`. - if (!this._mustAlwaysScroll && this._modalElement) { - const element = this._modalElement.nativeElement as Element; - - // The modal must scroll if the window height is smaller than the modal height + both margins. - this._mustScroll = window.innerHeight < element.clientHeight + margin * 2; + if (window) { + // Semantic UI modal margin is 3.5rem, which is relative to the global font size, so for compatibility: + const fontSize = parseFloat(window.getComputedStyle(document.documentElement).getPropertyValue("font-size")); + const margin = fontSize * 3.5; + + // _mustAlwaysScroll works by stopping _mustScroll from being automatically updated, so it stays `true`. + if (!this._mustAlwaysScroll && this._modalElement) { + const element = this._modalElement.nativeElement as Element; + + // The modal must scroll if the window height is smaller than the modal height + both margins. + this._mustScroll = window.innerHeight < element.clientHeight + margin * 2; + } } } - public onClick(e:MouseEvent):void { + public onClick(e:any):void { // Makes sense here, as the modal shouldn't be attached to any DOM element. e.stopPropagation(); } // Document listener is fine here because nobody will enough modals open. @HostListener("document:keyup", ["$event"]) - public onDocumentKeyUp(e:KeyboardEvent):void { + public onDocumentKeyup(e:any):void { if (e.keyCode === KeyCode.Escape) { // Close automatically covers case of `!isClosable`, so check not needed. this.close(); diff --git a/src/modules/popup/classes/popup-controller.ts b/src/modules/popup/classes/popup-controller.ts index 270e6636f..ebde023a6 100644 --- a/src/modules/popup/classes/popup-controller.ts +++ b/src/modules/popup/classes/popup-controller.ts @@ -40,7 +40,7 @@ export abstract class SuiPopupController implements IPopup, OnDestroy { // When the popup is closed (onClose fires on animation complete), this.popup.onClose.subscribe(() => this.cleanup()); - this._documentListener = renderer.listen("document", "click", (e:MouseEvent) => this.onDocumentClick(e)); + this._documentListener = renderer.listen("document", "click", (e:any) => this.onDocumentClick(e)); } public configure(config?:IPopupConfig):void { @@ -53,8 +53,10 @@ export abstract class SuiPopupController implements IPopup, OnDestroy { // Cancel the opening timer. clearTimeout(this._openingTimeout); - // Start the popup opening after the specified delay interval. - this._openingTimeout = window.setTimeout(() => this.open(), this.popup.config.delay); + if (window) { + // Start the popup opening after the specified delay interval. + this._openingTimeout = window.setTimeout(() => this.open(), this.popup.config.delay); + } } public open():void { @@ -141,7 +143,7 @@ export abstract class SuiPopupController implements IPopup, OnDestroy { } } - public onDocumentClick(e:MouseEvent):void { + public onDocumentClick(e:any):void { // If the popup trigger is outside click, if (this._componentRef && this.popup.config.trigger === PopupTrigger.OutsideClick) { const target = e.target as Element; diff --git a/src/modules/popup/components/popup.ts b/src/modules/popup/components/popup.ts index ea044c3c5..9a99f40dc 100644 --- a/src/modules/popup/components/popup.ts +++ b/src/modules/popup/components/popup.ts @@ -176,7 +176,7 @@ export class SuiPopup implements IPopup { public close():void { // Only attempt to close if currently open. - if (this.isOpen) { + if (this.isOpen && window) { // Cancel all other transitions, and initiate the closing transition. this.transitionController.stopAll(); this.transitionController.animate( @@ -193,7 +193,7 @@ export class SuiPopup implements IPopup { } @HostListener("click", ["$event"]) - public onClick(event:MouseEvent):void { + public onClick(event:any):void { // Makes sense here, as the popup shouldn't be attached to any DOM element. event.stopPropagation(); } diff --git a/src/modules/search/components/search.ts b/src/modules/search/components/search.ts index d6dc2cfc7..ae6979c08 100644 --- a/src/modules/search/components/search.ts +++ b/src/modules/search/components/search.ts @@ -202,7 +202,7 @@ export class SuiSearch implements AfterViewInit, OnDestroy { this.transition = "scale"; this.transitionDuration = 200; - this._documentClickListener = renderer.listen("document", "click", (e:MouseEvent) => this.onDocumentClick(e)); + this._documentClickListener = renderer.listen("document", "click", (e:any) => this.onDocumentClick(e)); } public ngAfterViewInit():void { @@ -226,7 +226,7 @@ export class SuiSearch implements AfterViewInit, OnDestroy { } } - public onClick(e:MouseEvent):void { + public onClick(e:any):void { this.open(); } @@ -251,7 +251,7 @@ export class SuiSearch implements AfterViewInit, OnDestroy { } } - public onDocumentClick(e:MouseEvent):void { + public onDocumentClick(e:any):void { if (!this._element.nativeElement.contains(e.target)) { this.dropdownService.setOpenState(false); } diff --git a/src/modules/search/services/search.service.ts b/src/modules/search/services/search.service.ts index fb6122fdd..df9e2286c 100644 --- a/src/modules/search/services/search.service.ts +++ b/src/modules/search/services/search.service.ts @@ -108,12 +108,14 @@ export class SearchService { this._query = query; clearTimeout(this._searchDelayTimeout); - this._searchDelayTimeout = window.setTimeout( - () => { - this.updateQuery(query, callback); - }, - this.searchDelay - ); + if (window) { + this._searchDelayTimeout = window.setTimeout( + () => { + this.updateQuery(query, callback); + }, + this.searchDelay + ); + } } // Updates the current search query. diff --git a/src/modules/select/classes/select-base.ts b/src/modules/select/classes/select-base.ts index 10f43eaac..a7a3c675f 100644 --- a/src/modules/select/classes/select-base.ts +++ b/src/modules/select/classes/select-base.ts @@ -243,7 +243,7 @@ export abstract class SuiSelectBase implements AfterContentInit, OnDestroy this.transitionDuration = 200; this.onTouched = new EventEmitter(); - this._documentKeyDownListener = renderer.listen("document", "keydown", (e:KeyboardEvent) => this.onDocumentKeyDown(e)); + this._documentKeyDownListener = renderer.listen("document", "keydown", (e:any) => this.onDocumentKeyDown(e)); this._selectClasses = true; } @@ -378,7 +378,7 @@ export abstract class SuiSelectBase implements AfterContentInit, OnDestroy } @HostListener("keypress", ["$event"]) - public onKeyPress(e:KeyboardEvent):void { + public onKeyPress(e:any):void { if (e.keyCode === KeyCode.Enter) { // Enables support for focussing and opening with the keyboard alone. // Using directly because Renderer2 doesn't have invokeElementMethod method anymore. @@ -386,7 +386,7 @@ export abstract class SuiSelectBase implements AfterContentInit, OnDestroy } } - public onDocumentKeyDown(e:KeyboardEvent):void { + public onDocumentKeyDown(e:any):void { if (this._element.nativeElement.contains(e.target) && !this.dropdownService.isOpen && e.keyCode === KeyCode.Down) { @@ -399,7 +399,7 @@ export abstract class SuiSelectBase implements AfterContentInit, OnDestroy } } - public onQueryInputKeydown(event:KeyboardEvent):void {} + public onQueryInputKeydown(event:any):void {} protected focus():void { if (this.isSearchable && this.searchInput) { diff --git a/src/modules/select/components/multi-select.ts b/src/modules/select/components/multi-select.ts index 770cb5563..372ad54fe 100644 --- a/src/modules/select/components/multi-select.ts +++ b/src/modules/select/components/multi-select.ts @@ -229,7 +229,7 @@ export class SuiMultiSelect extends SuiSelectBase implements ICustom } } - public onQueryInputKeydown(event:KeyboardEvent):void { + public onQueryInputKeydown(event:any):void { if (event.keyCode === KeyCode.Backspace && this.query === "" && this.selectedOptions.length > 0) { // Deselect the rightmost option when the user presses backspace in the search input. this.deselectOption(this.selectedOptions[this.selectedOptions.length - 1]); diff --git a/src/modules/select/directives/select-search.ts b/src/modules/select/directives/select-search.ts index dc6086001..da43819e2 100644 --- a/src/modules/select/directives/select-search.ts +++ b/src/modules/select/directives/select-search.ts @@ -15,11 +15,11 @@ export class SuiSelectSearch { } public onQueryUpdated:EventEmitter; - public onQueryKeyDown:EventEmitter; + public onQueryKeyDown:EventEmitter; constructor(private _renderer:Renderer2, private _element:ElementRef) { this.onQueryUpdated = new EventEmitter(); - this.onQueryKeyDown = new EventEmitter(); + this.onQueryKeyDown = new EventEmitter(); this._searchClass = true; this._autoComplete = "off"; @@ -31,7 +31,7 @@ export class SuiSelectSearch { } @HostListener("keydown", ["$event"]) - private onKeyDown(e:KeyboardEvent):void { + private onKeyDown(e:any):void { this.onQueryKeyDown.emit(e); } diff --git a/src/modules/sidebar/components/sidebar-sibling.ts b/src/modules/sidebar/components/sidebar-sibling.ts index b00cd81c8..147bb66f5 100644 --- a/src/modules/sidebar/components/sidebar-sibling.ts +++ b/src/modules/sidebar/components/sidebar-sibling.ts @@ -67,7 +67,7 @@ export class SuiSidebarSibling { } @HostListener("click", ["$event"]) - public onClick(event:MouseEvent):void { + public onClick(event:any):void { if (this.service.isVisible && !this.service.wasJustOpened) { this.service.setVisibleState(false); } diff --git a/src/modules/sidebar/services/sidebar.service.ts b/src/modules/sidebar/services/sidebar.service.ts index 729fe9f71..baf2b9a95 100644 --- a/src/modules/sidebar/services/sidebar.service.ts +++ b/src/modules/sidebar/services/sidebar.service.ts @@ -93,7 +93,9 @@ export class SidebarService { setTimeout(() => this.wasJustOpened = false); clearTimeout(this._isAnimatingTimeout); - this._isAnimatingTimeout = window.setTimeout(() => this.isAnimating = false, 500); + if (window) { + this._isAnimatingTimeout = window.setTimeout(() => this.isAnimating = false, 500); + } } } diff --git a/src/modules/transition/classes/transition-controller.ts b/src/modules/transition/classes/transition-controller.ts index cb99652db..3e7fdccac 100644 --- a/src/modules/transition/classes/transition-controller.ts +++ b/src/modules/transition/classes/transition-controller.ts @@ -131,7 +131,9 @@ export class TransitionController { } // Wait the length of the animation before calling the complete callback. - this._animationTimeout = window.setTimeout(() => this.finishTransition(transition), transition.duration); + if (window) { + this._animationTimeout = window.setTimeout(() => this.finishTransition(transition), transition.duration); + } } // Called when a transition has completed. diff --git a/src/sui.module.ts b/src/sui.module.ts index 651b09f07..a90070d3f 100644 --- a/src/sui.module.ts +++ b/src/sui.module.ts @@ -35,6 +35,8 @@ import { SuiUtilityModule } from "./misc"; +import "./misc/util/helpers/closest"; + @NgModule({ exports: [ // Collections diff --git a/tslint.json b/tslint.json index f330d42a7..961dcff5a 100644 --- a/tslint.json +++ b/tslint.json @@ -6,6 +6,7 @@ "element", ["sui", "demo", "example"], "kebab-case" - ] + ], + "no-invalid-this": false } }