From ef06e63823e8d02b230fece76641916623cf24a8 Mon Sep 17 00:00:00 2001 From: Oleg Pimenov Date: Thu, 27 Jun 2019 11:40:25 +0300 Subject: [PATCH] feat: add angular/cdk (#153) * fix(modal): Focus fields beyond the modal window #UIM-11 (#154) --- docs/getting-started.md | 4 +- package.json | 1 + packages/cdk/a11y/a11y-module.ts | 3 +- .../cdk/a11y/focus-monitor/focus-monitor.ts | 60 +- packages/cdk/accordion/accordion-item.spec.ts | 275 ----- packages/cdk/accordion/accordion-item.ts | 162 --- packages/cdk/accordion/accordion-module.ts | 15 - packages/cdk/accordion/accordion.spec.ts | 88 -- packages/cdk/accordion/accordion.ts | 61 - packages/cdk/accordion/index.ts | 1 - packages/cdk/accordion/public-api.ts | 4 - packages/cdk/accordion/tsconfig.build.json | 14 - packages/cdk/bidi/bidi-module.ts | 11 - packages/cdk/bidi/dir-document-token.ts | 28 - packages/cdk/bidi/dir.ts | 62 - packages/cdk/bidi/directionality.spec.ts | 157 --- packages/cdk/bidi/directionality.ts | 33 - packages/cdk/bidi/index.ts | 1 - packages/cdk/bidi/public-api.ts | 6 - packages/cdk/bidi/tsconfig.build.json | 14 - packages/cdk/coercion/array.spec.ts | 36 - packages/cdk/coercion/array.ts | 4 - .../cdk/coercion/boolean-property.spec.ts | 49 - packages/cdk/coercion/boolean-property.ts | 4 - packages/cdk/coercion/css-pixel-value.spec.ts | 20 - packages/cdk/coercion/css-pixel-value.ts | 7 - packages/cdk/coercion/index.ts | 1 - packages/cdk/coercion/number-property.spec.ts | 84 -- packages/cdk/coercion/number-property.ts | 18 - packages/cdk/coercion/public-api.ts | 5 - packages/cdk/coercion/tsconfig.build.json | 14 - packages/cdk/collections/array-data-source.ts | 18 - packages/cdk/collections/collection-viewer.ts | 19 - packages/cdk/collections/collections.md | 34 - packages/cdk/collections/data-source.ts | 33 - packages/cdk/collections/index.ts | 1 - packages/cdk/collections/public-api.ts | 11 - packages/cdk/collections/selection.spec.ts | 286 ----- packages/cdk/collections/selection.ts | 213 ---- packages/cdk/collections/tree-adapter.ts | 23 - packages/cdk/collections/tsconfig.build.json | 14 - .../unique-selection-dispatcher.spec.ts | 37 - .../unique-selection-dispatcher.ts | 48 - .../cdk/layout/breakpoints-observer.spec.ts | 184 --- packages/cdk/layout/breakpoints-observer.ts | 135 --- packages/cdk/layout/breakpoints.ts | 24 - packages/cdk/layout/index.ts | 2 - packages/cdk/layout/layout-module.ts | 6 - packages/cdk/layout/media-matcher.spec.ts | 47 - packages/cdk/layout/media-matcher.ts | 74 -- packages/cdk/layout/public-api.ts | 5 - packages/cdk/layout/tsconfig.build.json | 14 - packages/cdk/overlay/_overlay.scss | 139 --- .../overlay/fullscreen-overlay-container.ts | 87 -- packages/cdk/overlay/index.ts | 1 - .../overlay-keyboard-dispatcher.spec.ts | 167 --- .../keyboard/overlay-keyboard-dispatcher.ts | 100 -- packages/cdk/overlay/overlay-config.ts | 56 - .../cdk/overlay/overlay-container.spec.ts | 77 -- packages/cdk/overlay/overlay-container.ts | 69 -- .../cdk/overlay/overlay-directives.spec.ts | 433 ------- packages/cdk/overlay/overlay-directives.ts | 420 ------- packages/cdk/overlay/overlay-module.ts | 41 - packages/cdk/overlay/overlay-prebuilt.scss | 3 - packages/cdk/overlay/overlay-ref.ts | 382 ------ packages/cdk/overlay/overlay-reference.ts | 31 - packages/cdk/overlay/overlay.spec.ts | 690 ----------- packages/cdk/overlay/overlay.ts | 118 -- .../connected-position-strategy.spec.ts | 489 -------- .../position/connected-position-strategy.ts | 215 ---- .../overlay/position/connected-position.ts | 112 -- ...exible-connected-position-strategy.spec.ts | 988 ---------------- .../flexible-connected-position-strategy.ts | 1034 ----------------- .../position/global-position-strategy.spec.ts | 308 ----- .../position/global-position-strategy.ts | 193 --- .../position/overlay-position-builder.ts | 55 - .../cdk/overlay/position/position-strategy.ts | 17 - packages/cdk/overlay/position/scroll-clip.ts | 36 - packages/cdk/overlay/public-api.ts | 23 - .../scroll/block-scroll-strategy.spec.ts | 200 ---- .../overlay/scroll/block-scroll-strategy.ts | 85 -- .../scroll/close-scroll-strategy.spec.ts | 154 --- .../overlay/scroll/close-scroll-strategy.ts | 83 -- packages/cdk/overlay/scroll/index.ts | 12 - .../overlay/scroll/noop-scroll-strategy.ts | 15 - .../scroll/reposition-scroll-strategy.spec.ts | 133 --- .../scroll/reposition-scroll-strategy.ts | 78 -- .../overlay/scroll/scroll-strategy-options.ts | 53 - .../cdk/overlay/scroll/scroll-strategy.ts | 23 - packages/cdk/overlay/tsconfig.build.json | 14 - packages/cdk/platform/features/input-types.ts | 59 - .../platform/features/passive-listeners.ts | 31 - packages/cdk/platform/features/scrolling.ts | 77 -- packages/cdk/platform/index.ts | 2 - packages/cdk/platform/platform-module.ts | 5 - packages/cdk/platform/platform.md | 3 - packages/cdk/platform/platform.ts | 63 - packages/cdk/platform/public-api.ts | 6 - packages/cdk/platform/tsconfig.build.json | 14 - packages/cdk/portal/dom-portal-outlet.ts | 102 -- packages/cdk/portal/index.ts | 1 - packages/cdk/portal/portal-directives.ts | 161 --- packages/cdk/portal/portal-errors.ts | 48 - packages/cdk/portal/portal-injector.ts | 24 - packages/cdk/portal/portal.spec.ts | 608 ---------- packages/cdk/portal/portal.ts | 255 ---- packages/cdk/portal/public-api.ts | 11 - packages/cdk/portal/tsconfig.build.json | 14 - packages/cdk/scrolling/README.md | 14 - .../scrolling/fixed-size-virtual-scroll.ts | 219 ---- packages/cdk/scrolling/index.ts | 2 - packages/cdk/scrolling/public-api.ts | 9 - .../cdk/scrolling/scroll-dispatcher.spec.ts | 261 ----- packages/cdk/scrolling/scroll-dispatcher.ts | 167 --- packages/cdk/scrolling/scrollable.ts | 189 --- packages/cdk/scrolling/scrolling-module.ts | 38 - packages/cdk/scrolling/tsconfig.build.json | 14 - packages/cdk/scrolling/viewport-ruler.spec.ts | 156 --- packages/cdk/scrolling/viewport-ruler.ts | 144 --- packages/cdk/scrolling/virtual-for-of.ts | 390 ------- .../cdk/scrolling/virtual-scroll-strategy.ts | 44 - .../scrolling/virtual-scroll-viewport.html | 12 - .../scrolling/virtual-scroll-viewport.scss | 87 -- .../scrolling/virtual-scroll-viewport.spec.ts | 942 --------------- .../cdk/scrolling/virtual-scroll-viewport.ts | 388 ------- packages/cdk/style-manager/index.ts | 2 - packages/cdk/style-manager/public-api.ts | 2 - .../cdk/style-manager/style-manager.spec.ts | 46 - packages/cdk/style-manager/style-manager.ts | 47 - .../cdk/style-manager/tsconfig.build.json | 14 - .../cdk/tree/control/base-tree-control.ts | 2 +- packages/cdk/tree/control/tree-control.ts | 3 +- packages/cdk/tree/padding.ts | 4 +- packages/cdk/tree/tree._spec.ts | 8 +- packages/cdk/tree/tree.ts | 10 +- packages/dev-app/dev-app-module.ts | 2 +- packages/dev-app/mosaic-module.ts | 2 +- packages/dev-app/system-config.ts | 20 +- .../module.ts | 4 +- .../cdk-virtual-scroll-data-source/module.ts | 7 +- packages/mosaic-dev/tree/module.ts | 4 +- packages/mosaic-moment-adapter/package.json | 1 + .../autocomplete-trigger.directive.ts | 24 +- .../autocomplete/autocomplete.component.ts | 2 +- .../autocomplete/autocomplete.module.ts | 2 +- .../mosaic/autocomplete/autocomplete.spec.ts | 6 +- .../button-toggle/button-toggle.component.ts | 4 +- packages/mosaic/button/button.module.ts | 2 +- packages/mosaic/card/card.module.ts | 3 +- packages/mosaic/core/_core.scss | 2 +- .../core/common-behaviors/common-module.ts | 2 +- .../mosaic/core/common-behaviors/disabled.ts | 2 +- packages/mosaic/core/option/option.ts | 2 +- .../core/overlay/overlay-position-map.ts | 2 +- packages/mosaic/core/select/constants.ts | 4 +- .../mosaic/datepicker/calendar-header.spec.ts | 2 +- packages/mosaic/datepicker/calendar.spec.ts | 2 +- packages/mosaic/datepicker/calendar.ts | 4 +- .../mosaic/datepicker/datepicker-input.ts | 2 +- .../mosaic/datepicker/datepicker-module.ts | 4 +- .../mosaic/datepicker/datepicker-toggle.ts | 2 +- packages/mosaic/datepicker/datepicker.spec.ts | 6 +- packages/mosaic/datepicker/datepicker.ts | 30 +- packages/mosaic/datepicker/month-view.spec.ts | 2 +- packages/mosaic/datepicker/month-view.ts | 2 +- .../mosaic/datepicker/multi-year-view.spec.ts | 2 +- packages/mosaic/datepicker/multi-year-view.ts | 2 +- packages/mosaic/datepicker/year-view.spec.ts | 2 +- packages/mosaic/datepicker/year-view.ts | 2 +- packages/mosaic/divider/divider.component.ts | 2 +- packages/mosaic/dropdown/dropdown-content.ts | 2 +- packages/mosaic/dropdown/dropdown-panel.ts | 2 +- packages/mosaic/dropdown/dropdown-trigger.ts | 28 +- .../mosaic/dropdown/dropdown.component.ts | 20 +- packages/mosaic/dropdown/dropdown.module.ts | 2 +- packages/mosaic/dropdown/dropdown.spec.ts | 8 +- packages/mosaic/icon/icon.module.ts | 3 +- packages/mosaic/input/input.ts | 4 +- .../mosaic/list/list-selection.component.ts | 2 +- packages/mosaic/modal/modal.component.html | 6 +- packages/mosaic/modal/modal.component.ts | 2 +- packages/mosaic/modal/modal.module.ts | 16 +- packages/mosaic/modal/modal.service.ts | 8 +- packages/mosaic/modal/modal.spec.ts | 2 +- packages/mosaic/modal/modal.type.ts | 3 +- packages/mosaic/navbar/navbar.module.ts | 2 +- packages/mosaic/package.json | 1 + packages/mosaic/popover/popover.component.ts | 44 +- packages/mosaic/popover/popover.module.ts | 2 +- packages/mosaic/popover/popover.spec.ts | 2 +- .../progress-bar/progress-bar.module.ts | 3 +- .../progress-spinner.module.ts | 3 +- packages/mosaic/radio/radio.component.ts | 75 +- .../mosaic/select/select.component.spec.ts | 8 +- packages/mosaic/select/select.component.ts | 14 +- packages/mosaic/select/select.module.ts | 2 +- .../sidepanel-container.component.ts | 4 +- packages/mosaic/sidepanel/sidepanel-ref.ts | 2 +- packages/mosaic/sidepanel/sidepanel.module.ts | 4 +- .../mosaic/sidepanel/sidepanel.service.ts | 6 +- packages/mosaic/sidepanel/sidepanel.spec.ts | 2 +- .../mosaic/splitter/splitter.component.ts | 3 +- packages/mosaic/tabs/tab-body.spec.ts | 4 +- packages/mosaic/tabs/tab-body.ts | 4 +- packages/mosaic/tabs/tab-group.ts | 2 +- packages/mosaic/tabs/tab-header.spec.ts | 6 +- packages/mosaic/tabs/tab-header.ts | 6 +- packages/mosaic/tabs/tab-label.ts | 2 +- .../tabs/tab-nav-bar/tab-nav-bar.spec.ts | 2 +- packages/mosaic/tabs/tab.ts | 2 +- packages/mosaic/tabs/tabs.module.ts | 2 +- packages/mosaic/tags/tag-input.spec.ts | 4 +- packages/mosaic/tags/tag-input.ts | 2 +- .../mosaic/tags/tag-list.component.spec.ts | 2 +- packages/mosaic/tags/tag-list.component.ts | 6 +- packages/mosaic/tags/tag.component.spec.ts | 2 +- packages/mosaic/tags/tag.component.ts | 2 +- packages/mosaic/tags/tag.module.ts | 2 +- .../mosaic/textarea/textarea.component.ts | 3 +- .../mosaic/timepicker/timepicker.module.ts | 2 +- packages/mosaic/timepicker/timepicker.ts | 2 +- packages/mosaic/tooltip/tooltip.component.ts | 44 +- packages/mosaic/tooltip/tooltip.module.ts | 2 +- packages/mosaic/tooltip/tooltip.spec.ts | 2 +- .../tree-select/tree-select.component.spec.ts | 8 +- .../tree-select/tree-select.component.ts | 14 +- .../mosaic/tree-select/tree-select.module.ts | 2 +- .../tree/data-source/flat-data-source.ts | 4 +- .../tree/data-source/nested-data-source.ts | 4 +- packages/mosaic/tree/tree-option.ts | 4 +- packages/mosaic/tree/tree-selection.ts | 2 +- .../vertical-navbar/vertical-navbar.module.ts | 2 +- tests/karma-system-config.js | 20 +- tools/packages/rollup-globals.ts | 14 +- yarn.lock | 13 +- 235 files changed, 368 insertions(+), 14574 deletions(-) delete mode 100644 packages/cdk/accordion/accordion-item.spec.ts delete mode 100644 packages/cdk/accordion/accordion-item.ts delete mode 100644 packages/cdk/accordion/accordion-module.ts delete mode 100644 packages/cdk/accordion/accordion.spec.ts delete mode 100644 packages/cdk/accordion/accordion.ts delete mode 100644 packages/cdk/accordion/index.ts delete mode 100644 packages/cdk/accordion/public-api.ts delete mode 100644 packages/cdk/accordion/tsconfig.build.json delete mode 100644 packages/cdk/bidi/bidi-module.ts delete mode 100644 packages/cdk/bidi/dir-document-token.ts delete mode 100644 packages/cdk/bidi/dir.ts delete mode 100644 packages/cdk/bidi/directionality.spec.ts delete mode 100644 packages/cdk/bidi/directionality.ts delete mode 100644 packages/cdk/bidi/index.ts delete mode 100644 packages/cdk/bidi/public-api.ts delete mode 100644 packages/cdk/bidi/tsconfig.build.json delete mode 100644 packages/cdk/coercion/array.spec.ts delete mode 100644 packages/cdk/coercion/array.ts delete mode 100644 packages/cdk/coercion/boolean-property.spec.ts delete mode 100644 packages/cdk/coercion/boolean-property.ts delete mode 100644 packages/cdk/coercion/css-pixel-value.spec.ts delete mode 100644 packages/cdk/coercion/css-pixel-value.ts delete mode 100644 packages/cdk/coercion/index.ts delete mode 100644 packages/cdk/coercion/number-property.spec.ts delete mode 100644 packages/cdk/coercion/number-property.ts delete mode 100644 packages/cdk/coercion/public-api.ts delete mode 100644 packages/cdk/coercion/tsconfig.build.json delete mode 100644 packages/cdk/collections/array-data-source.ts delete mode 100644 packages/cdk/collections/collection-viewer.ts delete mode 100644 packages/cdk/collections/collections.md delete mode 100644 packages/cdk/collections/data-source.ts delete mode 100644 packages/cdk/collections/index.ts delete mode 100644 packages/cdk/collections/public-api.ts delete mode 100644 packages/cdk/collections/selection.spec.ts delete mode 100644 packages/cdk/collections/selection.ts delete mode 100644 packages/cdk/collections/tree-adapter.ts delete mode 100644 packages/cdk/collections/tsconfig.build.json delete mode 100644 packages/cdk/collections/unique-selection-dispatcher.spec.ts delete mode 100644 packages/cdk/collections/unique-selection-dispatcher.ts delete mode 100644 packages/cdk/layout/breakpoints-observer.spec.ts delete mode 100644 packages/cdk/layout/breakpoints-observer.ts delete mode 100644 packages/cdk/layout/breakpoints.ts delete mode 100644 packages/cdk/layout/index.ts delete mode 100644 packages/cdk/layout/layout-module.ts delete mode 100644 packages/cdk/layout/media-matcher.spec.ts delete mode 100644 packages/cdk/layout/media-matcher.ts delete mode 100644 packages/cdk/layout/public-api.ts delete mode 100644 packages/cdk/layout/tsconfig.build.json delete mode 100644 packages/cdk/overlay/_overlay.scss delete mode 100644 packages/cdk/overlay/fullscreen-overlay-container.ts delete mode 100644 packages/cdk/overlay/index.ts delete mode 100644 packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts delete mode 100644 packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts delete mode 100644 packages/cdk/overlay/overlay-config.ts delete mode 100644 packages/cdk/overlay/overlay-container.spec.ts delete mode 100644 packages/cdk/overlay/overlay-container.ts delete mode 100644 packages/cdk/overlay/overlay-directives.spec.ts delete mode 100644 packages/cdk/overlay/overlay-directives.ts delete mode 100644 packages/cdk/overlay/overlay-module.ts delete mode 100644 packages/cdk/overlay/overlay-prebuilt.scss delete mode 100644 packages/cdk/overlay/overlay-ref.ts delete mode 100644 packages/cdk/overlay/overlay-reference.ts delete mode 100644 packages/cdk/overlay/overlay.spec.ts delete mode 100644 packages/cdk/overlay/overlay.ts delete mode 100644 packages/cdk/overlay/position/connected-position-strategy.spec.ts delete mode 100644 packages/cdk/overlay/position/connected-position-strategy.ts delete mode 100644 packages/cdk/overlay/position/connected-position.ts delete mode 100644 packages/cdk/overlay/position/flexible-connected-position-strategy.spec.ts delete mode 100644 packages/cdk/overlay/position/flexible-connected-position-strategy.ts delete mode 100644 packages/cdk/overlay/position/global-position-strategy.spec.ts delete mode 100644 packages/cdk/overlay/position/global-position-strategy.ts delete mode 100644 packages/cdk/overlay/position/overlay-position-builder.ts delete mode 100644 packages/cdk/overlay/position/position-strategy.ts delete mode 100644 packages/cdk/overlay/position/scroll-clip.ts delete mode 100644 packages/cdk/overlay/public-api.ts delete mode 100644 packages/cdk/overlay/scroll/block-scroll-strategy.spec.ts delete mode 100644 packages/cdk/overlay/scroll/block-scroll-strategy.ts delete mode 100644 packages/cdk/overlay/scroll/close-scroll-strategy.spec.ts delete mode 100644 packages/cdk/overlay/scroll/close-scroll-strategy.ts delete mode 100644 packages/cdk/overlay/scroll/index.ts delete mode 100644 packages/cdk/overlay/scroll/noop-scroll-strategy.ts delete mode 100644 packages/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts delete mode 100644 packages/cdk/overlay/scroll/reposition-scroll-strategy.ts delete mode 100644 packages/cdk/overlay/scroll/scroll-strategy-options.ts delete mode 100644 packages/cdk/overlay/scroll/scroll-strategy.ts delete mode 100644 packages/cdk/overlay/tsconfig.build.json delete mode 100644 packages/cdk/platform/features/input-types.ts delete mode 100644 packages/cdk/platform/features/passive-listeners.ts delete mode 100644 packages/cdk/platform/features/scrolling.ts delete mode 100644 packages/cdk/platform/index.ts delete mode 100644 packages/cdk/platform/platform-module.ts delete mode 100644 packages/cdk/platform/platform.md delete mode 100644 packages/cdk/platform/platform.ts delete mode 100644 packages/cdk/platform/public-api.ts delete mode 100644 packages/cdk/platform/tsconfig.build.json delete mode 100644 packages/cdk/portal/dom-portal-outlet.ts delete mode 100644 packages/cdk/portal/index.ts delete mode 100644 packages/cdk/portal/portal-directives.ts delete mode 100644 packages/cdk/portal/portal-errors.ts delete mode 100644 packages/cdk/portal/portal-injector.ts delete mode 100644 packages/cdk/portal/portal.spec.ts delete mode 100644 packages/cdk/portal/portal.ts delete mode 100644 packages/cdk/portal/public-api.ts delete mode 100644 packages/cdk/portal/tsconfig.build.json delete mode 100644 packages/cdk/scrolling/README.md delete mode 100644 packages/cdk/scrolling/fixed-size-virtual-scroll.ts delete mode 100644 packages/cdk/scrolling/index.ts delete mode 100644 packages/cdk/scrolling/public-api.ts delete mode 100644 packages/cdk/scrolling/scroll-dispatcher.spec.ts delete mode 100644 packages/cdk/scrolling/scroll-dispatcher.ts delete mode 100644 packages/cdk/scrolling/scrollable.ts delete mode 100644 packages/cdk/scrolling/scrolling-module.ts delete mode 100644 packages/cdk/scrolling/tsconfig.build.json delete mode 100644 packages/cdk/scrolling/viewport-ruler.spec.ts delete mode 100644 packages/cdk/scrolling/viewport-ruler.ts delete mode 100644 packages/cdk/scrolling/virtual-for-of.ts delete mode 100644 packages/cdk/scrolling/virtual-scroll-strategy.ts delete mode 100644 packages/cdk/scrolling/virtual-scroll-viewport.html delete mode 100644 packages/cdk/scrolling/virtual-scroll-viewport.scss delete mode 100644 packages/cdk/scrolling/virtual-scroll-viewport.spec.ts delete mode 100644 packages/cdk/scrolling/virtual-scroll-viewport.ts delete mode 100644 packages/cdk/style-manager/index.ts delete mode 100644 packages/cdk/style-manager/public-api.ts delete mode 100644 packages/cdk/style-manager/style-manager.spec.ts delete mode 100644 packages/cdk/style-manager/style-manager.ts delete mode 100644 packages/cdk/style-manager/tsconfig.build.json diff --git a/docs/getting-started.md b/docs/getting-started.md index cf49c7be3..af0108a70 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,12 +4,12 @@ #### NPM ```bash -npm install --save @ptsecurity/mosaic @ptsecurity/cdk @ptsecurity/mosaic-icons @ptsecurity/mosaic-moment-adapter moment messageformat +npm install --save @angular/cdk @ptsecurity/mosaic @ptsecurity/cdk @ptsecurity/mosaic-icons @ptsecurity/mosaic-moment-adapter moment messageformat ``` #### Yarn ```bash -yarn add @ptsecurity/mosaic @ptsecurity/cdk @ptsecurity/mosaic-icons @ptsecurity/mosaic-moment-adapter moment messageformat +yarn add @angular/cdk @ptsecurity/mosaic @ptsecurity/cdk @ptsecurity/mosaic-icons @ptsecurity/mosaic-moment-adapter moment messageformat ``` #### Snapshots builds diff --git a/package.json b/package.json index 0995d5003..7d5cff450 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "requiredAngularVersion": ">=8.0.0", "dependencies": { "@angular/animations": "^8.0.0", + "@angular/cdk": "^8.0.1", "@angular/common": "^8.0.0", "@angular/compiler": "^8.0.0", "@angular/core": "^8.0.0", diff --git a/packages/cdk/a11y/a11y-module.ts b/packages/cdk/a11y/a11y-module.ts index 797e0329d..7e0e42789 100644 --- a/packages/cdk/a11y/a11y-module.ts +++ b/packages/cdk/a11y/a11y-module.ts @@ -1,7 +1,6 @@ - +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { CdkMonitorFocus, FOCUS_MONITOR_PROVIDER } from './focus-monitor/focus-monitor'; diff --git a/packages/cdk/a11y/focus-monitor/focus-monitor.ts b/packages/cdk/a11y/focus-monitor/focus-monitor.ts index 59ad42c00..68a1352f5 100644 --- a/packages/cdk/a11y/focus-monitor/focus-monitor.ts +++ b/packages/cdk/a11y/focus-monitor/focus-monitor.ts @@ -1,3 +1,4 @@ +import { Platform, supportsPassiveEventListeners } from '@angular/cdk/platform'; import { Directive, ElementRef, @@ -9,11 +10,8 @@ import { Output, SkipSelf } from '@angular/core'; - import { Observable, Subject, Subscription, of as observableOf } from 'rxjs'; -import { Platform, supportsPassiveEventListeners } from '@ptsecurity/cdk/platform'; - // Through trial and error (on iPhone 6S) they found // that a value of around 650ms seems appropriate. @@ -23,11 +21,11 @@ export const TOUCH_BUFFER_MS = 650; export type FocusOrigin = 'touch' | 'mouse' | 'keyboard' | 'program' | null; -type MonitoredElementInfo = { - unlisten: Function, - checkChildren: boolean, - subject: Subject -}; +interface MonitoredElementInfo { + unlisten: Function; + checkChildren: boolean; + subject: Subject; +} /** Monitors mouse and keyboard events to determine the cause of focus events. */ @@ -57,9 +55,6 @@ export class FocusMonitor implements OnDestroy { /** Map of elements being monitored to their info. */ private _elementInfo = new Map(); - /** A map of global objects to lists of current listeners. */ - private _unregisterGlobalListeners = () => {}; - /** The number of elements currently being monitored. */ private _monitoredElementCount = 0; @@ -87,7 +82,7 @@ export class FocusMonitor implements OnDestroy { // Create monitored element info. const info: MonitoredElementInfo = { unlisten: () => {}, - checkChildren: checkChildren, + checkChildren, subject: new Subject() }; this._elementInfo.set(element, info); @@ -145,6 +140,28 @@ export class FocusMonitor implements OnDestroy { this._elementInfo.forEach((_info, element) => this.stopMonitoring(element)); } + /** + * Handles blur events on a registered element. + * @param event The blur event. + * @param element The monitored element. + */ + _onBlur(event: FocusEvent, element: HTMLElement) { + // If we are counting child-element-focus as focused, make sure that we aren't just blurring in + // order to focus another child of the monitored element. + const elementInfo = this._elementInfo.get(element); + + if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node && + element.contains(event.relatedTarget))) { + return; + } + + this._setClasses(element); + elementInfo.subject.next(null); + } + + /** A map of global objects to lists of current listeners. */ + private _unregisterGlobalListeners = () => {}; + /** Register necessary event listeners on the document and window. */ private _registerGlobalListeners() { // Do nothing if we're not on the browser platform. @@ -315,25 +332,6 @@ export class FocusMonitor implements OnDestroy { this._lastFocusOrigin = origin; } - /** - * Handles blur events on a registered element. - * @param event The blur event. - * @param element The monitored element. - */ - _onBlur(event: FocusEvent, element: HTMLElement) { - // If we are counting child-element-focus as focused, make sure that we aren't just blurring in - // order to focus another child of the monitored element. - const elementInfo = this._elementInfo.get(element); - - if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node && - element.contains(event.relatedTarget))) { - return; - } - - this._setClasses(element); - elementInfo.subject.next(null); - } - private _emitOrigin(subject: Subject, origin: FocusOrigin) { this._ngZone.run(() => subject.next(origin)); } diff --git a/packages/cdk/accordion/accordion-item.spec.ts b/packages/cdk/accordion/accordion-item.spec.ts deleted file mode 100644 index 2668c03bf..000000000 --- a/packages/cdk/accordion/accordion-item.spec.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { Component } from '@angular/core'; -import { async, TestBed, ComponentFixture } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; - -import { CdkAccordionModule, CdkAccordionItem } from './public-api'; - - -// tslint:disable:no-empty -// tslint:disable:no-unbound-method -describe('CdkAccordionItem', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - BrowserAnimationsModule, - CdkAccordionModule - ], - declarations: [ - SingleItem, - ItemGroupWithoutAccordion, - ItemGroupWithAccordion - ] - }); - TestBed.compileComponents(); - })); - - describe('single item', () => { - let fixture: ComponentFixture; - let item: CdkAccordionItem; - - beforeEach(() => { - fixture = TestBed.createComponent(SingleItem); - item = fixture.debugElement - .query(By.directive(CdkAccordionItem)) - .injector.get(CdkAccordionItem); - }); - - describe('that is not disabled', () => { - beforeEach(() => { - item.disabled = false; - }); - - it('should toggle its expanded state', () => { - expect(item.expanded).toBe(false); - item.toggle(); - expect(item.expanded).toBe(true); - item.toggle(); - expect(item.expanded).toBe(false); - }); - - it('should set its expanded state to expanded', () => { - item.expanded = false; - item.open(); - expect(item.expanded).toBe(true); - }); - - it('should set its expanded state to closed', () => { - item.expanded = true; - item.close(); - expect(item.expanded).toBe(false); - }); - - it('should emit a closed event', () => { - item.open(); - fixture.detectChanges(); - spyOn(item.closed, 'emit'); - item.close(); - fixture.detectChanges(); - expect(item.closed.emit).toHaveBeenCalled(); - }); - - it('should not emit a closed event when the item is closed already', () => { - expect(item.expanded).toBe(false); - spyOn(item.closed, 'emit'); - item.close(); - fixture.detectChanges(); - expect(item.closed.emit).not.toHaveBeenCalled(); - }); - - it('should emit an opened event', () => { - spyOn(item.opened, 'emit'); - item.open(); - fixture.detectChanges(); - expect(item.opened.emit).toHaveBeenCalled(); - }); - - it('should emit a destroyed event', () => { - spyOn(item.destroyed, 'emit'); - item.ngOnDestroy(); - fixture.detectChanges(); - expect(item.destroyed.emit).toHaveBeenCalled(); - }); - }); - - describe('that is disabled', () => { - beforeEach(() => { - item.disabled = true; - }); - - it('should not toggle its expanded state', () => { - expect(item.expanded).toBe(false); - item.toggle(); - expect(item.expanded).toBe(false); - }); - - it('should not set its expanded state to expanded', () => { - item.expanded = false; - item.open(); - expect(item.expanded).toBe(false); - }); - - it('should not set its expanded state to closed', () => { - item.expanded = true; - item.close(); - expect(item.expanded).toBe(true); - }); - - it('should not emit a closed event', () => { - spyOn(item.closed, 'emit'); - item.close(); - fixture.detectChanges(); - expect(item.closed.emit).not.toHaveBeenCalled(); - }); - - it('should not emit an opened event', () => { - spyOn(item.opened, 'emit'); - item.open(); - fixture.detectChanges(); - expect(item.opened.emit).not.toHaveBeenCalled(); - }); - - it('should emit a destroyed event', () => { - spyOn(item.destroyed, 'emit'); - item.ngOnDestroy(); - fixture.detectChanges(); - expect(item.destroyed.emit).toHaveBeenCalled(); - }); - }); - - it('should emit to and complete the `destroyed` stream on destroy', () => { - const emitSpy = jasmine.createSpy('emit spy'); - const completeSpy = jasmine.createSpy('complete spy'); - const subscription = item.destroyed.subscribe(emitSpy, undefined, completeSpy); - - fixture.detectChanges(); - fixture.destroy(); - - expect(emitSpy).toHaveBeenCalled(); - expect(completeSpy).toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - - it('should complete the `opened` stream on destroy', () => { - const completeSpy = jasmine.createSpy('complete spy'); - const subscription = item.opened.subscribe(() => { - }, undefined, completeSpy); - - fixture.detectChanges(); - fixture.destroy(); - - expect(completeSpy).toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - - it('should complete the `closed` stream on destroy', () => { - const completeSpy = jasmine.createSpy('complete spy'); - const subscription = item.closed.subscribe(() => { - }, undefined, completeSpy); - - fixture.detectChanges(); - fixture.destroy(); - - expect(completeSpy).toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - - }); - - describe('items without accordion', () => { - let fixture: ComponentFixture; - let firstItem: CdkAccordionItem; - let secondItem: CdkAccordionItem; - - beforeEach(() => { - fixture = TestBed.createComponent(ItemGroupWithoutAccordion); - [firstItem, secondItem] = fixture.debugElement - .queryAll(By.directive(CdkAccordionItem)) - .map((el) => el.injector.get(CdkAccordionItem)); - }); - - it('should not change expanded state based on unrelated items', () => { - expect(firstItem.expanded).toBe(false); - expect(secondItem.expanded).toBe(false); - firstItem.open(); - fixture.detectChanges(); - expect(firstItem.expanded).toBe(true); - expect(secondItem.expanded).toBe(false); - secondItem.open(); - fixture.detectChanges(); - expect(firstItem.expanded).toBe(true); - expect(secondItem.expanded).toBe(true); - }); - - it('should not change expanded state for disabled items', () => { - firstItem.disabled = true; - expect(firstItem.expanded).toBe(false); - expect(secondItem.expanded).toBe(false); - firstItem.open(); - fixture.detectChanges(); - expect(firstItem.expanded).toBe(false); - expect(secondItem.expanded).toBe(false); - secondItem.open(); - fixture.detectChanges(); - expect(firstItem.expanded).toBe(false); - expect(secondItem.expanded).toBe(true); - }); - }); - - - describe('items in accordion', () => { - let fixture: ComponentFixture; - let firstItem: CdkAccordionItem; - let secondItem: CdkAccordionItem; - - beforeEach(() => { - fixture = TestBed.createComponent(ItemGroupWithAccordion); - [firstItem, secondItem] = fixture.debugElement - .queryAll(By.directive(CdkAccordionItem)) - .map((el) => el.injector.get(CdkAccordionItem)); - }); - - it('should change expanded state based on related items', () => { - expect(firstItem.expanded).toBe(false); - expect(secondItem.expanded).toBe(false); - firstItem.open(); - fixture.detectChanges(); - expect(firstItem.expanded).toBe(true); - expect(secondItem.expanded).toBe(false); - secondItem.open(); - fixture.detectChanges(); - expect(firstItem.expanded).toBe(false); - expect(secondItem.expanded).toBe(true); - }); - }); -}); - -@Component({ - template: ` - ` -}) -class SingleItem { -} - -@Component({ - template: ` - - - ` -}) -class ItemGroupWithoutAccordion { -} - -@Component({ - template: ` - - - - - ` -}) -class ItemGroupWithAccordion { -} diff --git a/packages/cdk/accordion/accordion-item.ts b/packages/cdk/accordion/accordion-item.ts deleted file mode 100644 index a3c8902df..000000000 --- a/packages/cdk/accordion/accordion-item.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { - Output, - Directive, - EventEmitter, - Input, - OnDestroy, - Optional, - ChangeDetectorRef, - SkipSelf -} from '@angular/core'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; -import { UniqueSelectionDispatcher } from '@ptsecurity/cdk/collections'; -import { Subscription } from 'rxjs'; - -import { CdkAccordion } from './accordion'; - - -/** Used to generate unique ID for each accordion item. */ -let nextId = 0; - -/** - * An basic directive expected to be extended and decorated as a component. Sets up all - * events and attributes needed to be managed by a CdkAccordion parent. - */ -@Directive({ - selector: 'cdk-accordion-item, [cdkAccordionItem]', - exportAs: 'cdkAccordionItem', - providers: [ - // Provide CdkAccordion as undefined to prevent nested accordion items from registering - // to the same accordion. - {provide: CdkAccordion, useValue: undefined} - ] -}) -export class CdkAccordionItem implements OnDestroy { - - /** Whether the AccordionItem is expanded. */ - @Input() - get expanded(): any { - return this._expanded; - } - - set expanded(expanded: any) { - // tslint:disable:no-parameter-reassignment - expanded = coerceBooleanProperty(expanded); - - // Only emit events and update the internal value if the value changes. - if (this._expanded !== expanded) { - this._expanded = expanded; - this.expandedChange.emit(expanded); - - if (expanded) { - this.opened.emit(); - /** - * In the unique selection dispatcher, the id parameter is the id of the CdkAccordionItem, - * the name value is the id of the accordion. - */ - const accordionId = this.accordion ? this.accordion.id : this.id; - this._expansionDispatcher.notify(this.id, accordionId); - } else { - this.closed.emit(); - } - - // Ensures that the animation will run when the value is set outside of an `@Input`. - // This includes cases like the open, close and toggle methods. - this._changeDetectorRef.markForCheck(); - } - } - - /** Whether the AccordionItem is disabled. */ - @Input() - get disabled() { - return this._disabled; - } - - set disabled(disabled: any) { - this._disabled = coerceBooleanProperty(disabled); - } - /** Event emitted every time the AccordionItem is closed. */ - @Output() closed: EventEmitter = new EventEmitter(); - /** Event emitted every time the AccordionItem is opened. */ - @Output() opened: EventEmitter = new EventEmitter(); - /** Event emitted when the AccordionItem is destroyed. */ - @Output() destroyed: EventEmitter = new EventEmitter(); - - /** - * Emits whenever the expanded state of the accordion changes. - * Primarily used to facilitate two-way binding. - * @docs-private - */ - @Output() expandedChange: EventEmitter = new EventEmitter(); - - /** The unique AccordionItem id. */ - readonly id: string = `cdk-accordion-child-${nextId++}`; - /** Subscription to openAll/closeAll events. */ - private openCloseAllSubscription = Subscription.EMPTY; - - private _expanded = false; - - private _disabled: boolean = false; - - constructor(@Optional() @SkipSelf() public accordion: CdkAccordion, - private _changeDetectorRef: ChangeDetectorRef, - protected _expansionDispatcher: UniqueSelectionDispatcher) { - - this.removeUniqueSelectionListener = - _expansionDispatcher.listen((id: string, accordionId: string) => { - if (this.accordion && !this.accordion.multi && - this.accordion.id === accordionId && this.id !== id) { - this.expanded = false; - } - }); - - // When an accordion item is hosted in an accordion, subscribe to open/close events. - if (this.accordion) { - this.openCloseAllSubscription = this.subscribeToOpenCloseAllActions(); - } - } - - /** Emits an event for the accordion item being destroyed. */ - ngOnDestroy() { - this.opened.complete(); - this.closed.complete(); - this.destroyed.emit(); - this.destroyed.complete(); - this.removeUniqueSelectionListener(); - this.openCloseAllSubscription.unsubscribe(); - } - - /** Toggles the expanded state of the accordion item. */ - toggle(): void { - if (!this.disabled) { - this.expanded = !this.expanded; - } - } - - /** Sets the expanded state of the accordion item to false. */ - close(): void { - if (!this.disabled) { - this.expanded = false; - } - } - - /** Sets the expanded state of the accordion item to true. */ - open(): void { - if (!this.disabled) { - this.expanded = true; - } - } - - /** Unregister function for _expansionDispatcher. */ - // tslint:disable:no-empty - private removeUniqueSelectionListener: () => void = () => {}; - - private subscribeToOpenCloseAllActions(): Subscription { - return this.accordion.openCloseAllActions.subscribe((expanded) => { - // Only change expanded state if item is enabled - if (!this.disabled) { - this.expanded = expanded; - } - }); - } -} diff --git a/packages/cdk/accordion/accordion-module.ts b/packages/cdk/accordion/accordion-module.ts deleted file mode 100644 index 62de4a924..000000000 --- a/packages/cdk/accordion/accordion-module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { CdkAccordion } from './accordion'; -import { CdkAccordionItem } from './accordion-item'; - - -@NgModule({ - exports: [ - CdkAccordion, CdkAccordionItem - ], - declarations: [ - CdkAccordion, CdkAccordionItem - ] -}) -export class CdkAccordionModule {} diff --git a/packages/cdk/accordion/accordion.spec.ts b/packages/cdk/accordion/accordion.spec.ts deleted file mode 100644 index c8fb98404..000000000 --- a/packages/cdk/accordion/accordion.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Component, ViewChild } from '@angular/core'; -import { async, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; - -import { CdkAccordionModule, CdkAccordionItem } from './public-api'; - - -describe('CdkAccordion', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - BrowserAnimationsModule, - CdkAccordionModule - ], - declarations: [ - SetOfItems, - NestedItems - ] - }); - TestBed.compileComponents(); - })); - - it('should ensure only one item is expanded at a time', () => { - const fixture = TestBed.createComponent(SetOfItems); - const [firstPanel, secondPanel] = fixture.debugElement - .queryAll(By.directive(CdkAccordionItem)) - .map((el) => el.injector.get(CdkAccordionItem)); - - firstPanel.open(); - fixture.detectChanges(); - expect(firstPanel.expanded).toBeTruthy(); - expect(secondPanel.expanded).toBeFalsy(); - - secondPanel.open(); - fixture.detectChanges(); - expect(firstPanel.expanded).toBeFalsy(); - expect(secondPanel.expanded).toBeTruthy(); - }); - - it('should allow multiple items to be expanded simultaneously', () => { - const fixture = TestBed.createComponent(SetOfItems); - const [firstPanel, secondPanel] = fixture.debugElement - .queryAll(By.directive(CdkAccordionItem)) - .map((el) => el.injector.get(CdkAccordionItem)); - - fixture.componentInstance.multi = true; - fixture.detectChanges(); - firstPanel.expanded = true; - secondPanel.expanded = true; - fixture.detectChanges(); - expect(firstPanel.expanded).toBeTruthy(); - expect(secondPanel.expanded).toBeTruthy(); - }); - - it('should not register nested items to the same accordion', () => { - const fixture = TestBed.createComponent(NestedItems); - const innerItem = fixture.componentInstance.innerItem; - const outerItem = fixture.componentInstance.outerItem; - - expect(innerItem.accordion).not.toBe(outerItem.accordion); - }); -}); - -@Component({ - template: ` - - - - ` -}) -class SetOfItems { - multi: boolean = false; -} - - -@Component({ - template: ` - - - - - ` -}) -class NestedItems { - @ViewChild('outerItem', {static: true}) outerItem: CdkAccordionItem; - @ViewChild('innerItem', {static: true}) innerItem: CdkAccordionItem; -} diff --git a/packages/cdk/accordion/accordion.ts b/packages/cdk/accordion/accordion.ts deleted file mode 100644 index 7f86dc7ef..000000000 --- a/packages/cdk/accordion/accordion.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Directive, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; -import { Subject } from 'rxjs'; - - -/** Used to generate unique ID for each accordion. */ -let nextId = 0; - -/** - * Directive whose purpose is to manage the expanded state of CdkAccordionItem children. - */ -@Directive({ - selector: 'cdk-accordion, [cdkAccordion]', - exportAs: 'cdkAccordion' -}) -export class CdkAccordion implements OnDestroy, OnChanges { - /** Emits when the state of the accordion changes */ - readonly stateChanges = new Subject(); - - /** Stream that emits true/false when openAll/closeAll is triggered. */ - readonly openCloseAllActions: Subject = new Subject(); - - /** A readonly id value to use for unique selection coordination. */ - readonly id = `cdk-accordion-${nextId++}`; - - /** Whether the accordion should allow multiple expanded accordion items simultaneously. */ - @Input() - get multi(): boolean { - return this._multi; - } - - set multi(multi: boolean) { - this._multi = coerceBooleanProperty(multi); - } - - private _multi: boolean = false; - - /** Opens all enabled accordion items in an accordion where multi is enabled. */ - openAll(): void { - this.openCloseAll(true); - } - - /** Closes all enabled accordion items in an accordion where multi is enabled. */ - closeAll(): void { - this.openCloseAll(false); - } - - ngOnChanges(changes: SimpleChanges) { - this.stateChanges.next(changes); - } - - ngOnDestroy() { - this.stateChanges.complete(); - } - - private openCloseAll(expanded: boolean): void { - if (this.multi) { - this.openCloseAllActions.next(expanded); - } - } -} diff --git a/packages/cdk/accordion/index.ts b/packages/cdk/accordion/index.ts deleted file mode 100644 index 1c48ad0a9..000000000 --- a/packages/cdk/accordion/index.ts +++ /dev/null @@ -1 +0,0 @@ - export * from './public-api'; diff --git a/packages/cdk/accordion/public-api.ts b/packages/cdk/accordion/public-api.ts deleted file mode 100644 index f0ad17203..000000000 --- a/packages/cdk/accordion/public-api.ts +++ /dev/null @@ -1,4 +0,0 @@ -export {CdkAccordionItem} from './accordion-item'; -export {CdkAccordion} from './accordion'; - -export * from './accordion-module'; diff --git a/packages/cdk/accordion/tsconfig.build.json b/packages/cdk/accordion/tsconfig.build.json deleted file mode 100644 index a440809c1..000000000 --- a/packages/cdk/accordion/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/accordion", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/bidi/bidi-module.ts b/packages/cdk/bidi/bidi-module.ts deleted file mode 100644 index b55fe8893..000000000 --- a/packages/cdk/bidi/bidi-module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { Dir } from './dir'; - - -@NgModule({ - exports: [Dir], - declarations: [Dir] -}) -export class BidiModule { -} diff --git a/packages/cdk/bidi/dir-document-token.ts b/packages/cdk/bidi/dir-document-token.ts deleted file mode 100644 index 5f989866e..000000000 --- a/packages/cdk/bidi/dir-document-token.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DOCUMENT } from '@angular/common'; -import { inject, InjectionToken } from '@angular/core'; - - -/** - * Injection token used to inject the document into Directionality. - * This is used so that the value can be faked in tests. - * - * We can't use the real document in tests because changing the real `dir` causes geometry-based - * tests in Safari to fail. - * - * We also can't re-provide the DOCUMENT token from platform-brower because the unit tests - * themselves use things like `querySelector` in test code. - * - * This token is defined in a separate file from Directionality as a workaround for - * https://github.com/angular/angular/issues/22559 - * - * @docs-private - */ -export const DIR_DOCUMENT = new InjectionToken('cdk-dir-doc', { - providedIn: 'root', - factory: DIR_DOCUMENT_FACTORY -}); - -/** @docs-private */ -export function DIR_DOCUMENT_FACTORY(): Document { - return inject(DOCUMENT); -} diff --git a/packages/cdk/bidi/dir.ts b/packages/cdk/bidi/dir.ts deleted file mode 100644 index 6d1faf90e..000000000 --- a/packages/cdk/bidi/dir.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - Directive, - Output, - Input, - EventEmitter, - AfterContentInit, - OnDestroy -} from '@angular/core'; - -import { Direction, Directionality } from './directionality'; - - -/** - * Directive to listen for changes of direction of part of the DOM. - * - * Provides itself as Directionality such that descendant directives only need to ever inject - * Directionality to get the closest direction. - */ -@Directive({ - selector: '[dir]', - providers: [{provide: Directionality, useExisting: Dir}], - host: {'[dir]': 'dir'}, - exportAs: 'dir' -}) -export class Dir implements Directionality, AfterContentInit, OnDestroy { - _dir: Direction = 'ltr'; - - /** Whether the `value` has been set to its initial value. */ - private _isInitialized: boolean = false; - - /** Event emitted when the direction changes. */ - @Output('dirChange') - change = new EventEmitter(); - - /** @docs-private */ - @Input() - get dir(): Direction { - return this._dir; - } - - set dir(value: Direction) { - const old = this._dir; - this._dir = (value === 'ltr' || value === 'rtl') ? value : 'ltr'; - - if (old !== this._dir && this._isInitialized) { - this.change.emit(this._dir); - } - } - - /** Current layout direction of the element. */ - get value(): Direction { return this.dir; } - - /** Initialize once default value has been set. */ - ngAfterContentInit() { - this._isInitialized = true; - } - - ngOnDestroy() { - this.change.complete(); - } -} - diff --git a/packages/cdk/bidi/directionality.spec.ts b/packages/cdk/bidi/directionality.spec.ts deleted file mode 100644 index 7888fc8cf..000000000 --- a/packages/cdk/bidi/directionality.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { Component, ViewChild } from '@angular/core'; -import { async, fakeAsync, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; - -import { BidiModule, Directionality, Dir, Direction, DIR_DOCUMENT } from './index'; - - -describe('Directionality', () => { - let fakeDocument: IFakeDocument; - - beforeEach(async(() => { - fakeDocument = {body: {}, documentElement: {}}; - - TestBed.configureTestingModule({ - imports: [BidiModule], - declarations: [ElementWithDir, InjectsDirectionality], - providers: [{provide: DIR_DOCUMENT, useFactory: () => fakeDocument}] - }).compileComponents(); - })); - - describe('Service', () => { - it('should read dir from the html element if not specified on the body', () => { - fakeDocument.documentElement.dir = 'rtl'; - - const fixture = TestBed.createComponent(InjectsDirectionality); - const testComponent = fixture.debugElement.componentInstance; - - expect(testComponent.dir.value).toBe('rtl'); - }); - - it('should read dir from the body even it is also specified on the html element', () => { - fakeDocument.documentElement.dir = 'ltr'; - fakeDocument.body.dir = 'rtl'; - - const fixture = TestBed.createComponent(InjectsDirectionality); - const testComponent = fixture.debugElement.componentInstance; - - expect(testComponent.dir.value).toBe('rtl'); - }); - - it('should default to ltr if nothing is specified on either body or the html element', () => { - const fixture = TestBed.createComponent(InjectsDirectionality); - const testComponent = fixture.debugElement.componentInstance; - - expect(testComponent.dir.value).toBe('ltr'); - }); - - it('should complete the `change` stream on destroy', () => { - const fixture = TestBed.createComponent(InjectsDirectionality); - const spy = jasmine.createSpy('complete spy'); - const subscription = - fixture.componentInstance.dir.change.subscribe(undefined, undefined, spy); - - fixture.componentInstance.dir.ngOnDestroy(); - expect(spy).toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - - it('should default to ltr if an invalid direction is set on the body', () => { - fakeDocument.body.dir = 'not-valid'; - - const fixture = TestBed.createComponent(InjectsDirectionality); - const testComponent = fixture.debugElement.componentInstance; - - expect(testComponent.dir.value).toBe('ltr'); - }); - - }); - - describe('Dir directive', () => { - it('should provide itself as Directionality', () => { - const fixture = TestBed.createComponent(ElementWithDir); - const injectedDirectionality = - fixture.debugElement.query(By.directive(InjectsDirectionality)).componentInstance.dir; - - fixture.detectChanges(); - - expect(injectedDirectionality.value).toBe('rtl'); - }); - - it('should emit a change event when the value changes', fakeAsync(() => { - const fixture = TestBed.createComponent(ElementWithDir); - const injectedDirectionality = - fixture.debugElement.query(By.directive(InjectsDirectionality)).componentInstance.dir; - - fixture.detectChanges(); - - let direction = injectedDirectionality.value; - injectedDirectionality.change.subscribe((dir: Direction) => { direction = dir; }); - - expect(direction).toBe('rtl'); - expect(injectedDirectionality.value).toBe('rtl'); - expect(fixture.componentInstance.changeCount).toBe(0); - - fixture.componentInstance.direction = 'ltr'; - - fixture.detectChanges(); - - expect(direction).toBe('ltr'); - expect(injectedDirectionality.value).toBe('ltr'); - expect(fixture.componentInstance.changeCount).toBe(1); - })); - - it('should complete the change stream on destroy', fakeAsync(() => { - const fixture = TestBed.createComponent(ElementWithDir); - const dir = - fixture.debugElement.query(By.directive(InjectsDirectionality)).componentInstance.dir; - const spy = jasmine.createSpy('complete spy'); - const subscription = dir.change.subscribe(undefined, undefined, spy); - - fixture.destroy(); - expect(spy).toHaveBeenCalled(); - subscription.unsubscribe(); - })); - - it('should default to ltr if an invalid value is passed in', () => { - const fixture = TestBed.createComponent(ElementWithDir); - - fixture.detectChanges(); - expect(fixture.componentInstance.dir.value).toBe('rtl'); - - fixture.componentInstance.direction = 'not-valid'; - fixture.detectChanges(); - expect(fixture.componentInstance.dir.value).toBe('ltr'); - }); - - }); -}); - - -@Component({ - template: ` -
- -
- ` -}) -class ElementWithDir { - @ViewChild(Dir, {static: false}) dir: Dir; - direction = 'rtl'; - changeCount = 0; -} - -/** Test component with Dir directive. */ -@Component({ - selector: 'injects-directionality', - template: `
` -}) -class InjectsDirectionality { - constructor(public dir: Directionality) { } -} - -interface IFakeDocument { - documentElement: {dir?: string}; - body: {dir?: string}; -} diff --git a/packages/cdk/bidi/directionality.ts b/packages/cdk/bidi/directionality.ts deleted file mode 100644 index 91407e8d0..000000000 --- a/packages/cdk/bidi/directionality.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { EventEmitter, Inject, Injectable, Optional, OnDestroy } from '@angular/core'; - -import { DIR_DOCUMENT } from './dir-document-token'; - - -export type Direction = 'ltr' | 'rtl'; - - -/** - * The directionality (LTR / RTL) context for the application (or a subtree of it). - * Exposes the current direction and a stream of direction changes. - */ -@Injectable({providedIn: 'root'}) -export class Directionality implements OnDestroy { - /** The current 'ltr' or 'rtl' value. */ - readonly value: Direction = 'ltr'; - - /** Stream that emits whenever the 'ltr' / 'rtl' state changes. */ - readonly change = new EventEmitter(); - - constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) { - if (_document) { - const bodyDir = _document.body ? _document.body.dir : null; - const htmlDir = _document.documentElement ? _document.documentElement.dir : null; - const value = bodyDir || htmlDir; - this.value = (value === 'ltr' || value === 'rtl') ? value : 'ltr'; - } - } - - ngOnDestroy() { - this.change.complete(); - } -} diff --git a/packages/cdk/bidi/index.ts b/packages/cdk/bidi/index.ts deleted file mode 100644 index 7e1a213e3..000000000 --- a/packages/cdk/bidi/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public-api'; diff --git a/packages/cdk/bidi/public-api.ts b/packages/cdk/bidi/public-api.ts deleted file mode 100644 index ae94fa7bd..000000000 --- a/packages/cdk/bidi/public-api.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { Directionality, Direction } from './directionality'; -export { DIR_DOCUMENT } from './dir-document-token'; -export { Dir } from './dir'; - -export * from './bidi-module'; - diff --git a/packages/cdk/bidi/tsconfig.build.json b/packages/cdk/bidi/tsconfig.build.json deleted file mode 100644 index 66efe1e12..000000000 --- a/packages/cdk/bidi/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/bidi", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/coercion/array.spec.ts b/packages/cdk/coercion/array.spec.ts deleted file mode 100644 index 45a6629d8..000000000 --- a/packages/cdk/coercion/array.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { coerceArray } from './array'; - - -describe('coerceArray', () => { - - it('should wrap a string in an array', () => { - const stringVal = 'just a string'; - expect(coerceArray(stringVal)).toEqual([stringVal]); - }); - - it('should wrap a number in an array', () => { - const numberVal = 42; - expect(coerceArray(numberVal)).toEqual([numberVal]); - }); - - it('should wrap an object in an array', () => { - const objectVal = {something: 'clever'}; - expect(coerceArray(objectVal)).toEqual([objectVal]); - }); - - it('should wrap a null vall in an array', () => { - const nullVal = null; - expect(coerceArray(nullVal)).toEqual([nullVal]); - }); - - it('should wrap an undefined value in an array', () => { - const undefinedVal = undefined; - expect(coerceArray(undefinedVal)).toEqual([undefinedVal]); - }); - - it('should not wrap an array in an array', () => { - const arrayVal = [1, 2, 3]; // tslint:disable-line - expect(coerceArray(arrayVal)).toBe(arrayVal); - }); - -}); diff --git a/packages/cdk/coercion/array.ts b/packages/cdk/coercion/array.ts deleted file mode 100644 index d8919f8b5..000000000 --- a/packages/cdk/coercion/array.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** Wraps the provided value in an array, unless the provided value is an array. */ -export function coerceArray(value: T | T[]): T[] { - return Array.isArray(value) ? value : [value]; -} diff --git a/packages/cdk/coercion/boolean-property.spec.ts b/packages/cdk/coercion/boolean-property.spec.ts deleted file mode 100644 index 49c2d0165..000000000 --- a/packages/cdk/coercion/boolean-property.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { coerceBooleanProperty } from './boolean-property'; - - -describe('coerceBooleanProperty', () => { - - it('should coerce undefined to false', () => { - expect(coerceBooleanProperty(undefined)).toBe(false); - }); - - it('should coerce null to false', () => { - expect(coerceBooleanProperty(null)).toBe(false); - }); - - it('should coerce the empty string to true', () => { - expect(coerceBooleanProperty('')).toBe(true); - }); - - it('should coerce zero to true', () => { - expect(coerceBooleanProperty(0)).toBe(true); - }); - - it('should coerce the string "false" to false', () => { - expect(coerceBooleanProperty('false')).toBe(false); - }); - - it('should coerce the boolean false to false', () => { - expect(coerceBooleanProperty(false)).toBe(false); - }); - - it('should coerce the boolean true to true', () => { - expect(coerceBooleanProperty(true)).toBe(true); - }); - - it('should coerce the string "true" to true', () => { - expect(coerceBooleanProperty('true')).toBe(true); - }); - - it('should coerce an arbitrary string to true', () => { - expect(coerceBooleanProperty('pink')).toBe(true); - }); - - it('should coerce an object to true', () => { - expect(coerceBooleanProperty({})).toBe(true); - }); - - it('should coerce an array to true', () => { - expect(coerceBooleanProperty([])).toBe(true); - }); -}); diff --git a/packages/cdk/coercion/boolean-property.ts b/packages/cdk/coercion/boolean-property.ts deleted file mode 100644 index e1ed63acd..000000000 --- a/packages/cdk/coercion/boolean-property.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** Coerces a data-bound value (typically a string) to a boolean. */ -export function coerceBooleanProperty(value: any): boolean { - return value != null && `${value}` !== 'false'; -} diff --git a/packages/cdk/coercion/css-pixel-value.spec.ts b/packages/cdk/coercion/css-pixel-value.spec.ts deleted file mode 100644 index c0461fe0e..000000000 --- a/packages/cdk/coercion/css-pixel-value.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { coerceCssPixelValue } from './css-pixel-value'; - - -describe('coerceCssPixelValue', () => { - it('should add pixel units to a number value', () => { - expect(coerceCssPixelValue(1337)).toBe('1337px'); // tslint:disable-line - }); - - it('should ignore string values', () => { - expect(coerceCssPixelValue('1337rem')).toBe('1337rem'); - }); - - it('should return an empty string for null', () => { - expect(coerceCssPixelValue(null)).toBe(''); - }); - - it('should return an empty string for undefined', () => { - expect(coerceCssPixelValue(undefined)).toBe(''); - }); -}); diff --git a/packages/cdk/coercion/css-pixel-value.ts b/packages/cdk/coercion/css-pixel-value.ts deleted file mode 100644 index a9f5c55e3..000000000 --- a/packages/cdk/coercion/css-pixel-value.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function coerceCssPixelValue(value: any): string { - if (value == null) { - return ''; - } - - return typeof value === 'string' ? value : `${value}px`; -} diff --git a/packages/cdk/coercion/index.ts b/packages/cdk/coercion/index.ts deleted file mode 100644 index 7e1a213e3..000000000 --- a/packages/cdk/coercion/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public-api'; diff --git a/packages/cdk/coercion/number-property.spec.ts b/packages/cdk/coercion/number-property.spec.ts deleted file mode 100644 index b5bb1b625..000000000 --- a/packages/cdk/coercion/number-property.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { coerceNumberProperty } from './number-property'; - - -//tslint:disable -describe('coerceNumberProperty', () => { - - it('should coerce undefined to 0 or default', () => { - expect(coerceNumberProperty(undefined)).toBe(0); - expect(coerceNumberProperty(undefined, 111)).toBe(111); - }); - - it('should coerce null to 0 or default', () => { - expect(coerceNumberProperty(null)).toBe(0); - expect(coerceNumberProperty(null, 111)).toBe(111); - }); - - it('should coerce true to 0 or default', () => { - expect(coerceNumberProperty(true)).toBe(0); - expect(coerceNumberProperty(true, 111)).toBe(111); - }); - - it('should coerce false to 0 or default', () => { - expect(coerceNumberProperty(false)).toBe(0); - expect(coerceNumberProperty(false, 111)).toBe(111); - - }); - - it('should coerce the empty string to 0 or default', () => { - expect(coerceNumberProperty('')).toBe(0); - expect(coerceNumberProperty('', 111)).toBe(111); - - }); - - it('should coerce the string "1" to 1', () => { - expect(coerceNumberProperty('1')).toBe(1); - expect(coerceNumberProperty('1', 111)).toBe(1); - }); - - it('should coerce the string "123.456" to 123.456', () => { - expect(coerceNumberProperty('123.456')).toBe(123.456); - expect(coerceNumberProperty('123.456', 111)).toBe(123.456); - }); - - it('should coerce the string "-123.456" to -123.456', () => { - expect(coerceNumberProperty('-123.456')).toBe(-123.456); - expect(coerceNumberProperty('-123.456', 111)).toBe(-123.456); - }); - - it('should coerce an arbitrary string to 0 or default', () => { - expect(coerceNumberProperty('pink')).toBe(0); - expect(coerceNumberProperty('pink', 111)).toBe(111); - }); - - it('should coerce an arbitrary string prefixed with a number to 0 or default', () => { - expect(coerceNumberProperty('123pink')).toBe(0); - expect(coerceNumberProperty('123pink', 111)).toBe(111); - }); - - it('should coerce the number 1 to 1', () => { - expect(coerceNumberProperty(1)).toBe(1); - expect(coerceNumberProperty(1, 111)).toBe(1); - }); - - it('should coerce the number 123.456 to 123.456', () => { - expect(coerceNumberProperty(123.456)).toBe(123.456); - expect(coerceNumberProperty(123.456, 111)).toBe(123.456); - }); - - it('should coerce the number -123.456 to -123.456', () => { - expect(coerceNumberProperty(-123.456)).toBe(-123.456); - expect(coerceNumberProperty(-123.456, 111)).toBe(-123.456); - }); - - it('should coerce an object to 0 or default', () => { - expect(coerceNumberProperty({})).toBe(0); - expect(coerceNumberProperty({}, 111)).toBe(111); - }); - - it('should coerce an array to 0 or default', () => { - expect(coerceNumberProperty([])).toBe(0); - expect(coerceNumberProperty([], 111)).toBe(111); - }); - -}); diff --git a/packages/cdk/coercion/number-property.ts b/packages/cdk/coercion/number-property.ts deleted file mode 100644 index 2b6c05b03..000000000 --- a/packages/cdk/coercion/number-property.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** Coerces a data-bound value (typically a string) to a number. */ -export function coerceNumberProperty(value: any): number; -export function coerceNumberProperty(value: any, fallback: D): number | D; -export function coerceNumberProperty(value: any, fallbackValue = 0) { - return _isNumberValue(value) ? Number(value) : fallbackValue; -} - -/** - * Whether the provided value is considered a number. - * @docs-private - */ -export function _isNumberValue(value: any): boolean { - // parseFloat(value) handles most of the cases we're interested in (it treats null, empty string, - // and other non-number values as NaN, where Number just uses 0) but it considers the string - // '123hello' to be a valid number. Therefore we also check if Number(value) is NaN. - // tslint:disable - return !isNaN(parseFloat(value as any)) && !isNaN(Number(value)); -} diff --git a/packages/cdk/coercion/public-api.ts b/packages/cdk/coercion/public-api.ts deleted file mode 100644 index 6a57c39ee..000000000 --- a/packages/cdk/coercion/public-api.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export * from './boolean-property'; -export * from './number-property'; -export * from './array'; -export * from './css-pixel-value'; diff --git a/packages/cdk/coercion/tsconfig.build.json b/packages/cdk/coercion/tsconfig.build.json deleted file mode 100644 index 7a988a44f..000000000 --- a/packages/cdk/coercion/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/coercion", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/collections/array-data-source.ts b/packages/cdk/collections/array-data-source.ts deleted file mode 100644 index 5a3bc7d44..000000000 --- a/packages/cdk/collections/array-data-source.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Observable, of as observableOf } from 'rxjs'; - -import { DataSource } from './data-source'; - - -/** DataSource wrapper for a native array. */ -export class ArrayDataSource extends DataSource { - constructor(private _data: T[] | Observable) { - super(); - } - - connect(): Observable { - return this._data instanceof Observable ? this._data : observableOf(this._data); - } - - // tslint:disable-next-line - disconnect() {} -} diff --git a/packages/cdk/collections/collection-viewer.ts b/packages/cdk/collections/collection-viewer.ts deleted file mode 100644 index 664339f66..000000000 --- a/packages/cdk/collections/collection-viewer.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Observable } from 'rxjs'; - - -/** Represents a range of numbers with a specified start and end. */ -// tslint:disable-next-line -export type ListRange = { start: number, end: number }; - - -/** - * Interface for any component that provides a view of some data collection and wants to provide - * information regarding the view and any changes made. - */ -export interface ICollectionViewer { - /** - * A stream that emits whenever the `ICollectionViewer` starts looking at a new portion of the - * data. The `start` index is inclusive, while the `end` is exclusive. - */ - viewChange: Observable; -} diff --git a/packages/cdk/collections/collections.md b/packages/cdk/collections/collections.md deleted file mode 100644 index cfbcda06c..000000000 --- a/packages/cdk/collections/collections.md +++ /dev/null @@ -1,34 +0,0 @@ -The `collections` package provides a set of utilities for managing collections. - -### `SelectionModel` -`SelectionModel` is a utility for powering selection of one or more options from a list. -This model is used in components such as the selection list. - -#### Basic Usage -```javascript -const model = new SelectionModel( - true, // multiple selection or not - [2,1,3] // initial selected values -); - -// select a value -model.select(4); -console.log(model.selected.length) //-> 4 - -// deselect a value -model.deselect(4); -console.log(model.selected.length) //-> 3 - -// toggle a value -model.toggle(4); -console.log(model.selected.length) //-> 4 - -// check for selection -console.log(model.isSelected(4)) //-> true - -// sort the selections -console.log(model.sort()) //-> [1,2,3,4] - -// listen for changes -model.changed.subscribe(s => console.log(s)); -``` diff --git a/packages/cdk/collections/data-source.ts b/packages/cdk/collections/data-source.ts deleted file mode 100644 index 9a6ce527f..000000000 --- a/packages/cdk/collections/data-source.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Observable } from 'rxjs'; - -import { ICollectionViewer } from './collection-viewer'; - - -export abstract class DataSource { - /** - * Connects a collection viewer (such as a data-table) to this data source. Note that - * the stream provided will be accessed during change detection and should not directly change - * values that are bound in template views. - * @param collectionViewer The component that exposes a view over the data provided by this - * data source. - * @returns Observable that emits a new value when the data changes. - */ - abstract connect(collectionViewer: ICollectionViewer): Observable; - - /** - * Disconnects a collection viewer (such as a data-table) from this data source. Can be used - * to perform any clean-up or tear-down operations when a view is being destroyed. - * - * @param collectionViewer The component that exposes a view over the data provided by this - * data source. - */ - abstract disconnect(collectionViewer: ICollectionViewer): void; -} - -/** Checks whether an object is a data source. */ -export function isDataSource(value: any): value is DataSource { - // Check if the value is a DataSource by observing if it has a connect function. Cannot - // be checked as an `instanceof DataSource` since people could create their own sources - // that match the interface, but don't extend DataSource. - return value && typeof value.connect === 'function'; -} diff --git a/packages/cdk/collections/index.ts b/packages/cdk/collections/index.ts deleted file mode 100644 index 7e1a213e3..000000000 --- a/packages/cdk/collections/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public-api'; diff --git a/packages/cdk/collections/public-api.ts b/packages/cdk/collections/public-api.ts deleted file mode 100644 index 4b6b30c13..000000000 --- a/packages/cdk/collections/public-api.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export * from './array-data-source'; -export * from './collection-viewer'; -export * from './data-source'; -export * from './tree-adapter'; -export * from './selection'; - -export { - UniqueSelectionDispatcher, - UniqueSelectionDispatcherListener -} from './unique-selection-dispatcher'; diff --git a/packages/cdk/collections/selection.spec.ts b/packages/cdk/collections/selection.spec.ts deleted file mode 100644 index bc688c9d4..000000000 --- a/packages/cdk/collections/selection.spec.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { getMultipleValuesInSingleSelectionError, SelectionModel } from './selection'; - - -// tslint:disable:no-magic-numbers -// tslint:disable no-unbound-method -describe('SelectionModel', () => { - describe('single selection', () => { - let model: SelectionModel; - - beforeEach(() => model = new SelectionModel()); - - it('should be able to select a single value', () => { - model.select(1); - - expect(model.selected.length).toBe(1); - expect(model.isSelected(1)).toBe(true); - }); - - it('should deselect the previously selected value', () => { - model.select(1); - model.select(2); - - expect(model.isSelected(1)).toBe(false); - expect(model.isSelected(2)).toBe(true); - }); - - it('should throw an error if multiple values are passed to model', () => { - expect(() => model.select(1, 2)).toThrow(getMultipleValuesInSingleSelectionError()); - }); - - it('should only preselect one value', () => { - model = new SelectionModel(false, [1, 2]); - - expect(model.selected.length).toBe(1); - expect(model.isSelected(1)).toBe(true); - expect(model.isSelected(2)).toBe(false); - }); - }); - - describe('multiple selection', () => { - let model: SelectionModel; - - beforeEach(() => model = new SelectionModel(true)); - - it('should be able to select multiple options', () => { - const onChangeSpy = jasmine.createSpy('onChange spy'); - - model.onChange!.subscribe(onChangeSpy); - model.select(1); - model.select(2); - - expect(model.selected.length).toBe(2); - expect(model.isSelected(1)).toBe(true); - expect(model.isSelected(2)).toBe(true); - expect(onChangeSpy).toHaveBeenCalledTimes(2); - }); - - it('should be able to select multiple options at the same time', () => { - const onChangeSpy = jasmine.createSpy('onChange spy'); - - model.onChange!.subscribe(onChangeSpy); - model.select(1, 2); - - expect(model.selected.length).toBe(2); - expect(model.isSelected(1)).toBe(true); - expect(model.isSelected(2)).toBe(true); - expect(onChangeSpy).toHaveBeenCalledTimes(1); - }); - - it('should be able to preselect multiple options', () => { - model = new SelectionModel(true, [1, 2]); - - expect(model.selected.length).toBe(2); - expect(model.isSelected(1)).toBe(true); - expect(model.isSelected(2)).toBe(true); - }); - - it('should be able to sort the selected values', () => { - model = new SelectionModel(true, [2, 3, 1]); - - expect(model.selected).toEqual([2, 3, 1]); - - model.sort(); - - expect(model.selected).toEqual([1, 2, 3]); - }); - - it('should sort values if `selected` has not been accessed before', () => { - model = new SelectionModel(true, [2, 3, 1]); - - // Important: don't assert `selected` before sorting so the getter isn't invoked - model.sort(); - expect(model.selected).toEqual([1, 2, 3]); - }); - }); - - describe('onChange event', () => { - it('should return the model that dispatched the event', () => { - const model = new SelectionModel(); - const spy = jasmine.createSpy('SelectionModel change event'); - - model.onChange.subscribe(spy); - model.select(1); - - const event = spy.calls.mostRecent().args[0]; - - expect(spy).toHaveBeenCalled(); - expect(event.source).toBe(model); - }); - - it('should return both the added and removed values', () => { - const model = new SelectionModel(); - const spy = jasmine.createSpy('SelectionModel change event'); - - model.select(1); - - model.onChange!.subscribe(spy); - - model.select(2); - - const event = spy.calls.mostRecent().args[0]; - - expect(spy).toHaveBeenCalled(); - expect(event.removed).toEqual([1]); - expect(event.added).toEqual([2]); - }); - - it('should have updated the selected value before emitting the change event', () => { - const model = new SelectionModel(true); - const spy = jasmine.createSpy('SelectionModel change event'); - - // Note: this assertion is only here to run the getter. - expect(model.selected).toEqual([]); - - model.onChange!.subscribe(() => spy(model.selected)); - model.select(1); - - expect(spy).toHaveBeenCalledWith([1]); - }); - - describe('selection', () => { - let model: SelectionModel; - let spy: jasmine.Spy; - - beforeEach(() => { - model = new SelectionModel(true); - spy = jasmine.createSpy('SelectionModel change event'); - - model.onChange!.subscribe(spy); - }); - - it('should emit an event when a value is selected', () => { - model.select(1); - - const event = spy.calls.mostRecent().args[0]; - - expect(spy).toHaveBeenCalled(); - expect(event.added).toEqual([1]); - expect(event.removed).toEqual([]); - }); - - it('should not emit multiple events for the same value', () => { - model.select(1); - model.select(1); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('should not emit an event when preselecting values', () => { - model = new SelectionModel(false, [1]); - spy = jasmine.createSpy('SelectionModel initial change event'); - model.onChange!.subscribe(spy); - - expect(spy).not.toHaveBeenCalled(); - }); - }); - - describe('deselection', () => { - let model: SelectionModel; - let spy: jasmine.Spy; - - beforeEach(() => { - model = new SelectionModel(true, [1, 2, 3]); - spy = jasmine.createSpy('SelectionModel change event'); - - model.onChange!.subscribe(spy); - }); - - it('should emit an event when a value is deselected', () => { - model.deselect(1); - - let event = spy.calls.mostRecent().args[0]; - - expect(spy).toHaveBeenCalled(); - expect(event.removed).toEqual([1]); - }); - - it('should not emit an event when a non-selected value is deselected', () => { - model.deselect(4); - expect(spy).not.toHaveBeenCalled(); - }); - - it('should emit a single event when clearing all of the selected options', () => { - model.clear(); - - const event = spy.calls.mostRecent().args[0]; - - expect(spy).toHaveBeenCalledTimes(1); - expect(event.removed).toEqual([1, 2, 3]); - }); - - }); - }); - - describe('disabling the change event', () => { - let model: SelectionModel; - - beforeEach(() => { - model = new SelectionModel(true, undefined, false); - }); - - it('should still update the select value', () => { - model.select(1); - expect(model.selected).toEqual([1]); - - model.select(2); - expect(model.selected).toEqual([1, 2]); - }); - }); - - it('should be able to determine whether it is empty', () => { - const model = new SelectionModel(); - - expect(model.isEmpty()).toBe(true); - - model.select(1); - - expect(model.isEmpty()).toBe(false); - }); - - it('should be able to determine whether it has a value', () => { - const model = new SelectionModel(); - - expect(model.hasValue()).toBe(false); - - model.select(1); - - expect(model.hasValue()).toBe(true); - }); - - it('should be able to toggle an option', () => { - const model = new SelectionModel(); - - model.toggle(1); - expect(model.isSelected(1)).toBe(true); - - model.toggle(1); - expect(model.isSelected(1)).toBe(false); - }); - - it('should be able to clear the selected options', () => { - const model = new SelectionModel(true); - - model.select(1); - model.select(2); - - expect(model.selected.length).toBe(2); - - model.clear(); - - expect(model.selected.length).toBe(0); - expect(model.isEmpty()).toBe(true); - }); - - it('should be empty if an empty array is passed for the preselected values', () => { - expect(new SelectionModel(false, []).selected).toEqual([]); - }); - - it('should be able to determine whether multiple values can be selected', () => { - const multipleSelectionModel = new SelectionModel(true); - expect(multipleSelectionModel.isMultipleSelection()).toBe(true); - - const singleSelectionModel = new SelectionModel(); - expect(singleSelectionModel.isMultipleSelection()).toBe(false); - }); -}); diff --git a/packages/cdk/collections/selection.ts b/packages/cdk/collections/selection.ts deleted file mode 100644 index 4c6a37c61..000000000 --- a/packages/cdk/collections/selection.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { Subject } from 'rxjs'; - - -/** - * Class to be used to power selecting one or more options from a list. - */ -export class SelectionModel { - - /** Event emitted when the value has changed. */ - changed: Subject> = new Subject(); - - /** - * Event emitted when the value has changed. - * @deprecated Use `changed` instead. - * @breaking-change 8.0.0 To be changed to `changed` - */ - onChange: Subject> = this.changed; - /** Currently-selected values. */ - selection = new Set(); - - /** Keeps track of the deselected options that haven't been emitted by the change event. */ - private deselectedToEmit: T[] = []; - - /** Keeps track of the selected options that haven't been emitted by the change event. */ - private selectedToEmit: T[] = []; - - get selected(): T[] { - if (!this._selected) { - this._selected = Array.from(this.selection.values()); - } - - return this._selected; - } - - private _selected: T[] | null; - - constructor( - private _multiple = false, - initiallySelectedValues?: T[], - private _emitChanges: boolean = true - ) { - if (initiallySelectedValues && initiallySelectedValues.length) { - if (_multiple) { - initiallySelectedValues.forEach((value) => this.markSelected(value)); - } else { - this.markSelected(initiallySelectedValues[0]); - } - - // Clear the array in order to avoid firing the change event for preselected values. - this.selectedToEmit.length = 0; - } - } - - /** - * Selects a value or an array of values. - */ - select(...values: T[]): void { - this.verifyValueAssignment(values); - - values.forEach((value) => this.markSelected(value)); - - this.emitChangeEvent(); - } - - /** - * Deselects a value or an array of values. - */ - deselect(...values: T[]): void { - this.verifyValueAssignment(values); - - values.forEach((value) => this.unmarkSelected(value)); - - this.emitChangeEvent(); - } - - /** - * Toggles a value between selected and deselected. - */ - toggle(value: T): void { - if (this.isSelected(value)) { - this.deselect(value); - } else { - this.select(value); - } - } - - /** - * Clears all of the selected values. - */ - clear(): void { - this.unmarkAll(); - this.emitChangeEvent(); - } - - /** - * Determines whether a value is selected. - */ - isSelected(value: T): boolean { - return this.selection.has(value); - } - - /** - * Determines whether the model does not have a value. - */ - isEmpty(): boolean { - return this.selection.size === 0; - } - - /** - * Determines whether the model has a value. - */ - hasValue(): boolean { - return !this.isEmpty(); - } - - /** - * Sorts the selected values based on a predicate function. - */ - sort(predicate?: (a: T, b: T) => number): void { - if (this._multiple && this.selected) { - this._selected!.sort(predicate); - } - } - - /** - * Gets whether multiple values can be selected. - */ - isMultipleSelection() { - return this._multiple; - } - - /** Emits a change event and clears the records of selected and deselected values. */ - private emitChangeEvent() { - // Clear the selected values so they can be re-cached. - this._selected = null; - - if (this.selectedToEmit.length || this.deselectedToEmit.length) { - this.changed.next({ - source: this, - added: this.selectedToEmit, - removed: this.deselectedToEmit - }); - - this.deselectedToEmit = []; - this.selectedToEmit = []; - } - } - - /** Selects a value. */ - private markSelected(value: T) { - if (!this.isSelected(value)) { - if (!this._multiple) { - this.unmarkAll(); - } - - this.selection.add(value); - - if (this._emitChanges) { - this.selectedToEmit.push(value); - } - } - } - - /** Deselects a value. */ - private unmarkSelected(value: T) { - if (this.isSelected(value)) { - this.selection.delete(value); - - if (this._emitChanges) { - this.deselectedToEmit.push(value); - } - } - } - - /** Clears out the selected values. */ - private unmarkAll() { - if (!this.isEmpty()) { - this.selection.forEach((value) => this.unmarkSelected(value)); - } - } - - /** - * Verifies the value assignment and throws an error if the specified value array is - * including multiple values while the selection model is not supporting multiple values. - */ - private verifyValueAssignment(values: T[]) { - if (values.length > 1 && !this._multiple) { - throw getMultipleValuesInSingleSelectionError(); - } - } -} - -/** - * Event emitted when the value of a MatSelectionModel has changed. - * @docs-private - */ -export interface SelectionChange { - /** Model that dispatched the event. */ - source: SelectionModel; - /** Options that were added to the model. */ - added: T[]; - /** Options that were removed from the model. */ - removed: T[]; -} - -/** - * Returns an error that reports that multiple values are passed into a selection model - * with a single value. - * @docs-private - */ -export function getMultipleValuesInSingleSelectionError() { - return Error('Cannot pass multiple values into SelectionModel with single-value mode.'); -} diff --git a/packages/cdk/collections/tree-adapter.ts b/packages/cdk/collections/tree-adapter.ts deleted file mode 100644 index 6534736af..000000000 --- a/packages/cdk/collections/tree-adapter.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { SelectionModel } from './selection'; - - -/** - * Interface for a class that can flatten hierarchical structured data and re-expand the flattened - * data back into its original structure. Should be used in conjunction with the cdk-tree. - */ -export interface ITreeDataNodeFlattener { - /** Transforms a set of hierarchical structured data into a flattened data array. */ - flattenNodes(structuredData: any[]): T[]; - - /** - * Expands a flattened array of data into its hierarchical form using the provided expansion - * model. - */ - expandFlattenedNodes(nodes: T[], expansionModel: SelectionModel): T[]; - - /** - * Put node descendants of node in array. - * If `onlyExpandable` is true, then only process expandable descendants. - */ - nodeDescendents(node: T, nodes: T[], onlyExpandable: boolean); -} diff --git a/packages/cdk/collections/tsconfig.build.json b/packages/cdk/collections/tsconfig.build.json deleted file mode 100644 index 8460a29e5..000000000 --- a/packages/cdk/collections/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/collections", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/collections/unique-selection-dispatcher.spec.ts b/packages/cdk/collections/unique-selection-dispatcher.spec.ts deleted file mode 100644 index e45bd2dd9..000000000 --- a/packages/cdk/collections/unique-selection-dispatcher.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { UniqueSelectionDispatcher } from './unique-selection-dispatcher'; - - -describe('Unique selection dispatcher', () => { - let dispatcher: UniqueSelectionDispatcher; - - beforeEach(() => dispatcher = new UniqueSelectionDispatcher()); - - it('should notify registered listeners', () => { - const spy = jasmine.createSpy('listen handler'); - - dispatcher.listen(spy); - dispatcher.notify('id', 'name'); - - expect(spy).toHaveBeenCalledWith('id', 'name'); - }); - - it('should not notify unregistered listeners', () => { - const spy = jasmine.createSpy('listen handler'); - const unregister = dispatcher.listen(spy); - - unregister(); - dispatcher.notify('id', 'name'); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('should remove all listeners when destroyed', () => { - const spy = jasmine.createSpy('listen handler'); - dispatcher.listen(spy); - - dispatcher.ngOnDestroy(); - dispatcher.notify('id', 'name'); - - expect(spy).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/cdk/collections/unique-selection-dispatcher.ts b/packages/cdk/collections/unique-selection-dispatcher.ts deleted file mode 100644 index 75ec985b1..000000000 --- a/packages/cdk/collections/unique-selection-dispatcher.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Injectable, OnDestroy } from '@angular/core'; - - -// Users of the Dispatcher never need to see this type, but TypeScript requires it to be exported. -export type UniqueSelectionDispatcherListener = (id: string, name: string) => void; - -/** - * Class to coordinate unique selection based on name. - * Intended to be consumed as an Angular service. - * This service is needed because native radio change events are only fired on the item currently - * being selected, and we still need to uncheck the previous selection. - * - * This service does not *store* any IDs and names because they may change at any time, so it is - * less error-prone if they are simply passed through when the events occur. - */ -@Injectable({providedIn: 'root'}) -export class UniqueSelectionDispatcher implements OnDestroy { - private _listeners: UniqueSelectionDispatcherListener[] = []; - - /** - * Notify other items that selection for the given name has been set. - * @param id ID of the item. - * @param name Name of the item. - */ - notify(id: string, name: string) { - for (const listener of this._listeners) { - listener(id, name); - } - } - - /** - * Listen for future changes to item selection. - * @return Function used to deregister listener - */ - listen(listener: UniqueSelectionDispatcherListener): () => void { - this._listeners.push(listener); - - return () => { - this._listeners = this._listeners.filter((registered: UniqueSelectionDispatcherListener) => { - return listener !== registered; - }); - }; - } - - ngOnDestroy() { - this._listeners = []; - } -} diff --git a/packages/cdk/layout/breakpoints-observer.spec.ts b/packages/cdk/layout/breakpoints-observer.spec.ts deleted file mode 100644 index 07feaa888..000000000 --- a/packages/cdk/layout/breakpoints-observer.spec.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Injectable } from '@angular/core'; -import { fakeAsync, TestBed, inject, flush } from '@angular/core/testing'; - -import { BreakpointObserver, IBreakpointState } from './breakpoints-observer'; -import { LayoutModule } from './layout-module'; -import { MediaMatcher } from './media-matcher'; - - -/* tslint:disable:no-magic-numbers */ -describe('BreakpointObserver', () => { - let breakpointManager: BreakpointObserver; - let mediaMatcher: FakeMediaMatcher; - - beforeEach(fakeAsync(() => { - TestBed.configureTestingModule({ - imports: [LayoutModule], - providers: [{provide: MediaMatcher, useClass: FakeMediaMatcher}] - }); - })); - - beforeEach(inject( - [BreakpointObserver, MediaMatcher], - (bm: BreakpointObserver, mm: FakeMediaMatcher) => { - breakpointManager = bm; - mediaMatcher = mm; - })); - - afterEach(() => { - mediaMatcher.clear(); - }); - - it('retrieves the whether a query is currently matched', fakeAsync(() => { - const query = 'everything starts as true in the FakeMediaMatcher'; - expect(breakpointManager.isMatched(query)).toBeTruthy(); - })); - - it('reuses the same MediaQueryList for matching queries', fakeAsync(() => { - expect(mediaMatcher.queryCount).toBe(0); - breakpointManager.observe('query1'); - expect(mediaMatcher.queryCount).toBe(1); - breakpointManager.observe('query1'); - expect(mediaMatcher.queryCount).toBe(1); - breakpointManager.observe('query2'); - expect(mediaMatcher.queryCount).toBe(2); - breakpointManager.observe('query1'); - expect(mediaMatcher.queryCount).toBe(2); - })); - - it('splits combined query strings into individual matchMedia listeners', fakeAsync(() => { - expect(mediaMatcher.queryCount).toBe(0); - breakpointManager.observe('query1, query2'); - expect(mediaMatcher.queryCount).toBe(2); - breakpointManager.observe('query1'); - expect(mediaMatcher.queryCount).toBe(2); - breakpointManager.observe('query2, query3'); - expect(mediaMatcher.queryCount).toBe(3); - })); - - it('accepts an array of queries', fakeAsync(() => { - const queries = ['1 query', '2 query', 'red query', 'blue query']; - breakpointManager.observe(queries); - expect(mediaMatcher.queryCount).toBe(queries.length); - })); - - it('completes all events when the breakpoint manager is destroyed', fakeAsync(() => { - const firstTest = jasmine.createSpy('test1'); - breakpointManager.observe('test1').subscribe({complete: firstTest}); - const secondTest = jasmine.createSpy('test2'); - breakpointManager.observe('test2').subscribe({complete: secondTest}); - - flush(); - expect(firstTest).not.toHaveBeenCalled(); - expect(secondTest).not.toHaveBeenCalled(); - - breakpointManager.ngOnDestroy(); - flush(); - - expect(firstTest).toHaveBeenCalled(); - expect(secondTest).toHaveBeenCalled(); - })); - - it('emits an event on the observable when values change', fakeAsync(() => { - const query = '(width: 999px)'; - let queryMatchState = false; - breakpointManager.observe(query).subscribe((state: IBreakpointState) => { - queryMatchState = state.matches; - }); - - flush(); - expect(queryMatchState).toBeTruthy(); - mediaMatcher.setMatchesQuery(query, false); - flush(); - expect(queryMatchState).toBeFalsy(); - })); - - it('emits an event on the observable with the matching state of all queries provided', - fakeAsync(() => { - const queryOne = '(width: 999px)'; - const queryTwo = '(width: 700px)'; - let state: IBreakpointState = {matches: false, breakpoints: {}}; - breakpointManager.observe([queryOne, queryTwo]).subscribe((breakpoint: IBreakpointState) => { - state = breakpoint; - }); - - mediaMatcher.setMatchesQuery(queryOne, false); - mediaMatcher.setMatchesQuery(queryTwo, false); - flush(); - expect(state.breakpoints).toEqual({[queryOne]: false, [queryTwo]: false}); - - mediaMatcher.setMatchesQuery(queryOne, true); - mediaMatcher.setMatchesQuery(queryTwo, false); - flush(); - expect(state.breakpoints).toEqual({[queryOne]: true, [queryTwo]: false}); - })); - - it('emits a true matches state when the query is matched', fakeAsync(() => { - const query = '(width: 999px)'; - breakpointManager.observe(query).subscribe(); - mediaMatcher.setMatchesQuery(query, true); - expect(breakpointManager.isMatched(query)).toBeTruthy(); - })); - - it('emits a false matches state when the query is not matched', fakeAsync(() => { - const query = '(width: 999px)'; - breakpointManager.observe(query).subscribe(); - mediaMatcher.setMatchesQuery(query, false); - expect(breakpointManager.isMatched(query)).toBeFalsy(); - })); -}); - -export class FakeMediaQueryList { - /** The callback for change events. */ - addListenerCallback?: (mql: MediaQueryListEvent) => void; - - constructor(public matches, public media) {} - - /** Toggles the matches state and "emits" a change event. */ - setMatches(matches: boolean) { - this.matches = matches; - this.addListenerCallback!(this as any); - } - - /** Registers the callback method for change events. */ - addListener(callback: (mql: MediaQueryListEvent) => void) { - this.addListenerCallback = callback; - } - - // Noop removal method for testing. - // tslint:disable-next-line - removeListener() { } -} - -@Injectable() -export class FakeMediaMatcher { - /** A map of match media queries. */ - private queries = new Map(); - - /** The number of distinct queries created in the media matcher during a test. */ - get queryCount(): number { - return this.queries.size; - } - - /** Fakes the match media response to be controlled in tests. */ - matchMedia(query: string): FakeMediaQueryList { - const mql = new FakeMediaQueryList(true, query); - this.queries.set(query, mql); - - return mql; - } - - /** Clears all queries from the map of queries. */ - clear() { - this.queries.clear(); - } - - /** Toggles the matching state of the provided query. */ - setMatchesQuery(query: string, matches: boolean) { - if (this.queries.has(query)) { - this.queries.get(query)!.setMatches(matches); - } else { - throw Error('This query is not being observed.'); - } - } -} diff --git a/packages/cdk/layout/breakpoints-observer.ts b/packages/cdk/layout/breakpoints-observer.ts deleted file mode 100644 index dcdb0efbd..000000000 --- a/packages/cdk/layout/breakpoints-observer.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Injectable, NgZone, OnDestroy } from '@angular/core'; -import { coerceArray } from '@ptsecurity/cdk/coercion'; -import { asapScheduler, combineLatest, fromEventPattern, Observable, Subject } from 'rxjs'; -import { debounceTime, map, startWith, takeUntil } from 'rxjs/operators'; - -import { MediaMatcher } from './media-matcher'; - - -/** The current state of a layout breakpoint. */ -export interface IBreakpointState { - /** Whether the breakpoint is currently matching. */ - matches: boolean; - - /** - * A key boolean pair for each query provided to the observe method, - * with its current matched state. - */ - breakpoints: { - [key: string]: boolean; - }; -} - -/** The current state of a layout breakpoint. */ -interface InternalBreakpointState { - /** Whether the breakpoint is currently matching. */ - matches: boolean; - /** The media query being to be matched */ - query: string; -} - -interface IQuery { - observable: Observable; - mql: MediaQueryList; -} - -/** Utility for checking the matching state of @media queries. */ -@Injectable({providedIn: 'root'}) -export class BreakpointObserver implements OnDestroy { - /** A map of all media queries currently being listened for. */ - private _queries: Map = new Map(); - /** A subject for all other observables to takeUntil based on. */ - private _destroySubject = new Subject(); - - constructor(private mediaMatcher: MediaMatcher, private zone: NgZone) { - } - - /** Completes the active subject, signalling to all other observables to complete. */ - ngOnDestroy() { - this._destroySubject.next(); - this._destroySubject.complete(); - } - - /** - * Whether one or more media queries match the current viewport size. - * @param value One or more media queries to check. - * @returns Whether any of the media queries match. - */ - isMatched(value: string | string[]): boolean { - const queries = splitQueries(coerceArray(value)); - - return queries.some((mediaQuery) => this._registerQuery(mediaQuery).mql.matches); - } - - /** - * Gets an observable of results for the given queries that will emit new results for any changes - * in matching of the given queries. - * @param value One or more media queries to check. - * @returns A stream of matches for the given queries. - */ - observe(value: string | string[]): Observable { - const queries = splitQueries(coerceArray(value)); - const observables = queries.map((query) => this._registerQuery(query).observable); - - return combineLatest(observables).pipe( - debounceTime(0, asapScheduler), - map((breakpointStates: InternalBreakpointState[]) => { - const response: IBreakpointState = { - matches: false, - breakpoints: {} - }; - - breakpointStates.forEach((state: InternalBreakpointState) => { - response.matches = response.matches || state.matches; - response.breakpoints[state.query] = state.matches; - }); - - return response; - })); - } - - /** Registers a specific query to be listened for. */ - private _registerQuery(query: string): IQuery { - // Only set up a new MediaQueryList if it is not already being listened for. - if (this._queries.has(query)) { - return this._queries.get(query)!; //tslint:disable-line - } - - const mql: MediaQueryList = this.mediaMatcher.matchMedia(query); - let queryListener; - - // Create callback for match changes and add it is as a listener. - const queryObservable = fromEventPattern( - // Listener callback methods are wrapped to be placed back in ngZone. Callbacks must be placed - // back into the zone because matchMedia is only included in Zone.js by loading the - // webapis-media-query.js file alongside the zone.js file. Additionally, some browsers do not - // have MediaQueryList inherit from EventTarget, which causes inconsistencies in how Zone.js - // patches it. - (listener: Function) => { - queryListener = (e: any) => this.zone.run(() => listener(e)); - mql.addListener(queryListener); - }, - () => mql.removeListener(queryListener)) - .pipe( - takeUntil(this._destroySubject), - startWith(mql), - map((nextMql: MediaQueryList) => ({query, matches: nextMql.matches})) - ); - - // Add the MediaQueryList to the set of queries. - const output = {observable: queryObservable, mql: mql}; //tslint:disable-line - this._queries.set(query, output); - - return output; - } -} - -/** - * Split each query string into separate query strings if two queries are provided as comma - * separated. - */ -function splitQueries(queries: string[]): string[] { - return queries.map((query: string) => query.split(',')) - .reduce((a1: string[], a2: string[]) => a1.concat(a2)) - .map((query) => query.trim()); -} diff --git a/packages/cdk/layout/breakpoints.ts b/packages/cdk/layout/breakpoints.ts deleted file mode 100644 index f927b933e..000000000 --- a/packages/cdk/layout/breakpoints.ts +++ /dev/null @@ -1,24 +0,0 @@ -// PascalCase is being used as Breakpoints is used like an enum. -// tslint:disable-next-line:variable-name -export const Breakpoints = { - XSmall: '(max-width: 599px)', - Small: '(min-width: 600px) and (max-width: 959px)', - Medium: '(min-width: 960px) and (max-width: 1279px)', - Large: '(min-width: 1280px) and (max-width: 1919px)', - XLarge: '(min-width: 1920px)', - - Handset: '(max-width: 599px) and (orientation: portrait), ' + - '(max-width: 959px) and (orientation: landscape)', - Tablet: '(min-width: 600px) and (max-width: 839px) and (orientation: portrait), ' + - '(min-width: 960px) and (max-width: 1279px) and (orientation: landscape)', - Web: '(min-width: 840px) and (orientation: portrait), ' + - '(min-width: 1280px) and (orientation: landscape)', - - HandsetPortrait: '(max-width: 599px) and (orientation: portrait)', - TabletPortrait: '(min-width: 600px) and (max-width: 839px) and (orientation: portrait)', - WebPortrait: '(min-width: 840px) and (orientation: portrait)', - - HandsetLandscape: '(max-width: 959px) and (orientation: landscape)', - TabletLandscape: '(min-width: 960px) and (max-width: 1279px) and (orientation: landscape)', - WebLandscape: '(min-width: 1280px) and (orientation: landscape)' -}; diff --git a/packages/cdk/layout/index.ts b/packages/cdk/layout/index.ts deleted file mode 100644 index 2530908a6..000000000 --- a/packages/cdk/layout/index.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export * from './public-api'; diff --git a/packages/cdk/layout/layout-module.ts b/packages/cdk/layout/layout-module.ts deleted file mode 100644 index 19755eda3..000000000 --- a/packages/cdk/layout/layout-module.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { NgModule } from '@angular/core'; - - -@NgModule() -export class LayoutModule {} diff --git a/packages/cdk/layout/media-matcher.spec.ts b/packages/cdk/layout/media-matcher.spec.ts deleted file mode 100644 index 30b3fe0f5..000000000 --- a/packages/cdk/layout/media-matcher.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { async, TestBed, inject } from '@angular/core/testing'; -import { Platform } from '@ptsecurity/cdk/platform'; - -import { LayoutModule } from './index'; -import { MediaMatcher } from './media-matcher'; - - -describe('MediaMatcher', () => { - let mediaMatcher: MediaMatcher; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [LayoutModule] - }); - })); - - beforeEach(inject([MediaMatcher], (mm: MediaMatcher) => { - mediaMatcher = mm; - })); - - it('correctly returns a MediaQueryList to check for matches', () => { - expect(mediaMatcher.matchMedia('(min-width: 1px)').matches).toBeTruthy(); - expect(mediaMatcher.matchMedia('(max-width: 1px)').matches).toBeFalsy(); - }); - - it('should add CSS rules for provided queries when the platform is webkit', - inject([Platform], (platform: Platform) => { - const randomWidth = `${Math.random()}px`; //tslint:disable-line - - expect(getStyleTagByString(randomWidth)).toBeFalsy(); - mediaMatcher.matchMedia(`(width: ${randomWidth})`); - - if (platform.WEBKIT) { - expect(getStyleTagByString(randomWidth)).toBeTruthy(); - } else { - expect(getStyleTagByString(randomWidth)).toBeFalsy(); - } - - function getStyleTagByString(str: string): HTMLStyleElement | undefined { - return Array.from(document.head!.querySelectorAll('style')).find((tag) => { - const rules = tag.sheet ? Array.from((tag.sheet as CSSStyleSheet).cssRules) : []; - - return !!rules.find((rule) => rule.cssText.includes(str)); - }); - } - })); -}); diff --git a/packages/cdk/layout/media-matcher.ts b/packages/cdk/layout/media-matcher.ts deleted file mode 100644 index 6f0a79b5a..000000000 --- a/packages/cdk/layout/media-matcher.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Platform } from '@ptsecurity/cdk/platform'; - - -/** Global registry for all dynamically-created, injected media queries. */ -const mediaQueriesForWebkitCompatibility: Set = new Set(); - -/** Style tag that holds all of the dynamically-created media queries. */ -let mediaQueryStyleNode: HTMLStyleElement | undefined; - -/** A utility for calling matchMedia queries. */ -@Injectable({providedIn: 'root'}) -export class MediaMatcher { - /** The internal matchMedia method to return back a MediaQueryList like object. */ - private _matchMedia: (query: string) => MediaQueryList; - - constructor(private platform: Platform) { - this._matchMedia = this.platform.isBrowser && window.matchMedia ? - // matchMedia is bound to the window scope intentionally as it is an illegal invocation to - // call it from a different scope. - window.matchMedia.bind(window) : - noopMatchMedia; - } - - /** - * Evaluates the given media query and returns the native MediaQueryList from which results - * can be retrieved. - * Confirms the layout engine will trigger for the selector query provided and returns the - * MediaQueryList for the query provided. - */ - matchMedia(query: string): MediaQueryList { - if (this.platform.WEBKIT) { - createEmptyStyleRule(query); - } - - return this._matchMedia(query); - } -} - -/** - * For Webkit engines that only trigger the MediaQueryListListener when - * there is at least one CSS selector for the respective media query. - */ -function createEmptyStyleRule(query: string) { - if (mediaQueriesForWebkitCompatibility.has(query)) { - return; - } - - try { - if (!mediaQueryStyleNode) { - mediaQueryStyleNode = document.createElement('style'); - mediaQueryStyleNode.setAttribute('type', 'text/css'); - document.head!.appendChild(mediaQueryStyleNode); - } - - if (mediaQueryStyleNode.sheet) { - (mediaQueryStyleNode.sheet as CSSStyleSheet) - .insertRule(`@media ${query} {.fx-query-test{ }}`, 0); - mediaQueriesForWebkitCompatibility.add(query); - } - } catch (e) { - console.error(e); //tslint:disable-line - } -} - -/** No-op matchMedia replacement for non-browser platforms. */ -function noopMatchMedia(query: string): MediaQueryList { - return { - matches: query === 'all' || query === '', - media: query, - addListener: () => {}, //tslint:disable-line - removeListener: () => {} //tslint:disable-line - } as any; -} diff --git a/packages/cdk/layout/public-api.ts b/packages/cdk/layout/public-api.ts deleted file mode 100644 index 4b953b14e..000000000 --- a/packages/cdk/layout/public-api.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export {LayoutModule} from './layout-module'; -export {BreakpointObserver, IBreakpointState} from './breakpoints-observer'; -export {Breakpoints} from './breakpoints'; -export {MediaMatcher} from './media-matcher'; diff --git a/packages/cdk/layout/tsconfig.build.json b/packages/cdk/layout/tsconfig.build.json deleted file mode 100644 index d9f462946..000000000 --- a/packages/cdk/layout/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/layout", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/overlay/_overlay.scss b/packages/cdk/overlay/_overlay.scss deleted file mode 100644 index efb8cb07f..000000000 --- a/packages/cdk/overlay/_overlay.scss +++ /dev/null @@ -1,139 +0,0 @@ -// We want overlays to always appear over user content, so set a baseline -// very high z-index for the overlay container, which is where we create the new -// stacking context for all overlays. -$cdk-z-index-overlay-container: 1000; -$cdk-z-index-overlay: 1000; -$cdk-z-index-overlay-backdrop: 1000; - -// Background color for all of the backdrops -$cdk-overlay-dark-backdrop-background: rgba(0, 0, 0, 0.288); - -// Default backdrop animation is based on the Material Design swift-ease-out. -$backdrop-animation-duration: 400ms !default; -$backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default; - -@mixin cdk-overlay() { - .cdk-overlay-container, .cdk-global-overlay-wrapper { - // Disable events from being captured on the overlay container. - pointer-events: none; - - // The container should be the size of the viewport. - top: 0; - left: 0; - height: 100%; - width: 100%; - } - - // The overlay-container is an invisible element which contains all individual overlays. - .cdk-overlay-container { - position: fixed; - z-index: $cdk-z-index-overlay-container; - - &:empty { - // Hide the element when it doesn't have any child nodes. This doesn't - // include overlays that have been detached, rather than disposed. - display: none; - } - } - - // We use an extra wrapper element in order to use make the overlay itself a flex item. - // This makes centering the overlay easy without running into the subpixel rendering - // problems tied to using `transform` and without interfering with the other position - // strategies. - .cdk-global-overlay-wrapper { - display: flex; - position: absolute; - z-index: $cdk-z-index-overlay; - } - - // A single overlay pane. - .cdk-overlay-pane { - // Note: it's important for this one to start off `absolute`, - // in order for us to be able to measure it correctly. - position: absolute; - pointer-events: auto; - box-sizing: border-box; - z-index: $cdk-z-index-overlay; - - // For connected-position overlays, we set `display: flex` in - // order to force `max-width` and `max-height` to take effect. - display: flex; - max-width: 100%; - max-height: 100%; - } - - .cdk-overlay-backdrop { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - - z-index: $cdk-z-index-overlay-backdrop; - pointer-events: auto; - -webkit-tap-highlight-color: transparent; - transition: opacity $backdrop-animation-duration $backdrop-animation-timing-function; - opacity: 0; - - &.cdk-overlay-backdrop-showing { - opacity: 1; - - // In high contrast mode the rgba background will become solid so we need to fall back - // to making it opaque using `opacity`. Note that we can't use the `cdk-high-contrast` - // mixin, because we can't normalize the import path to the _a11y.scss both for the - // source and when this file is distributed. See #10908. - @media screen and (-ms-high-contrast: active) { - opacity: 0.6; - } - } - } - - .cdk-overlay-dark-backdrop { - background: $cdk-overlay-dark-backdrop-background; - } - - .cdk-overlay-transparent-backdrop { - // Note: as of Firefox 57, having the backdrop be `background: none` will prevent it from - // capturing the user's mouse scroll events. Since we also can't use something like - // `rgba(0, 0, 0, 0)`, we work around the inconsistency by not setting the background at - // all and using `opacity` to make the element transparent. - &, &.cdk-overlay-backdrop-showing { - opacity: 0; - } - } - - // Overlay parent element used with the connected position strategy. Used to constrain the - // overlay element's size to fit within the viewport. - .cdk-overlay-connected-position-bounding-box { - position: absolute; - z-index: $cdk-z-index-overlay; - - // We use `display: flex` on this element exclusively for centering connected overlays. - // When *not* centering, a top/left/bottom/right will be set which overrides the normal - // flex layout. - display: flex; - - // We use the `column` direction here to avoid some flexbox issues in Edge - // when using the "grow after open" options. - flex-direction: column; - - // Add some dimensions so the element has an `innerText` which some people depend on in tests. - min-width: 1px; - min-height: 1px; - } - - // Used when disabling global scrolling. - .cdk-global-scrollblock { - position: fixed; - - // Necessary for the content not to lose its width. Note that we're using 100%, instead of - // 100vw, because 100vw includes the width plus the scrollbar, whereas 100% is the width - // that the element had before we made it `fixed`. - width: 100%; - - // Note: this will always add a scrollbar to whatever element it is on, which can - // potentially result in double scrollbars. It shouldn't be an issue, because we won't - // block scrolling on a page that doesn't have a scrollbar in the first place. - overflow-y: scroll; - } -} diff --git a/packages/cdk/overlay/fullscreen-overlay-container.ts b/packages/cdk/overlay/fullscreen-overlay-container.ts deleted file mode 100644 index 1dc4e0666..000000000 --- a/packages/cdk/overlay/fullscreen-overlay-container.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { DOCUMENT } from '@angular/common'; -import { Injectable, Inject, OnDestroy } from '@angular/core'; - -import { OverlayContainer } from './overlay-container'; - - -/** - * Alternative to OverlayContainer that supports correct displaying of overlay elements in - * Fullscreen mode - * https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen - * - * Should be provided in the root component. - */ -@Injectable() -export class FullscreenOverlayContainer extends OverlayContainer implements OnDestroy { - private _fullScreenEventName: string | undefined; - private _fullScreenListener: () => void; - - constructor(@Inject(DOCUMENT) _document: any) { - super(_document); - } - - ngOnDestroy() { - super.ngOnDestroy(); - - if (this._fullScreenEventName && this._fullScreenListener) { - this._document.removeEventListener(this._fullScreenEventName, this._fullScreenListener); - } - } - - /** - * When the page is put into fullscreen mode, a specific element is specified. - * Only that element and its children are visible when in fullscreen mode. - */ - getFullscreenElement(): Element { - return this._document.fullscreenElement || - this._document.webkitFullscreenElement || - (this._document as any).mozFullScreenElement || - (this._document as any).msFullscreenElement || - null; - } - - protected _createContainer(): void { - super._createContainer(); - this._adjustParentForFullscreenChange(); - this._addFullscreenChangeListener(() => this._adjustParentForFullscreenChange()); - } - - private _adjustParentForFullscreenChange(): void { - if (!this._containerElement) { - return; - } - - const fullscreenElement = this.getFullscreenElement(); - const parent = fullscreenElement || this._document.body; - parent.appendChild(this._containerElement); - } - - private _addFullscreenChangeListener(fn: () => void) { - const eventName = this._getEventName(); - - if (eventName) { - if (this._fullScreenListener) { - this._document.removeEventListener(eventName, this._fullScreenListener); - } - - this._document.addEventListener(eventName, fn); - this._fullScreenListener = fn; - } - } - - private _getEventName(): string | undefined { - if (!this._fullScreenEventName) { - if (this._document.fullscreenEnabled) { - this._fullScreenEventName = 'fullscreenchange'; - } else if (this._document.webkitFullscreenEnabled) { - this._fullScreenEventName = 'webkitfullscreenchange'; - } else if ((this._document as any).mozFullScreenEnabled) { - this._fullScreenEventName = 'mozfullscreenchange'; - } else if ((this._document as any).msFullscreenEnabled) { - this._fullScreenEventName = 'MSFullscreenChange'; - } - } - - return this._fullScreenEventName; - } -} diff --git a/packages/cdk/overlay/index.ts b/packages/cdk/overlay/index.ts deleted file mode 100644 index 7e1a213e3..000000000 --- a/packages/cdk/overlay/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public-api'; diff --git a/packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts b/packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts deleted file mode 100644 index 00a89036d..000000000 --- a/packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { Component, NgModule } from '@angular/core'; -import { TestBed, inject } from '@angular/core/testing'; -import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { ComponentPortal } from '@ptsecurity/cdk/portal'; -import { dispatchKeyboardEvent } from '@ptsecurity/cdk/testing'; - -import { OverlayModule, OverlayContainer, Overlay } from '../index'; - -import { OverlayKeyboardDispatcher } from './overlay-keyboard-dispatcher'; - - -describe('OverlayKeyboardDispatcher', () => { - let keyboardDispatcher: OverlayKeyboardDispatcher; - let overlay: Overlay; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [OverlayModule, TestComponentModule] - }); - - inject([OverlayKeyboardDispatcher, Overlay], (kbd: OverlayKeyboardDispatcher, o: Overlay) => { - keyboardDispatcher = kbd; - overlay = o; - })(); - }); - - afterEach(inject([OverlayContainer], (overlayContainer: OverlayContainer) => { - overlayContainer.ngOnDestroy(); - })); - - it('should track overlays in order as they are attached and detached', () => { - const overlayOne = overlay.create(); - const overlayTwo = overlay.create(); - const EXPECTED_MAGIC_NUMBER = 2; - - // Attach overlays - keyboardDispatcher.add(overlayOne); - keyboardDispatcher.add(overlayTwo); - - expect(keyboardDispatcher._attachedOverlays.length) - .toBe(EXPECTED_MAGIC_NUMBER, 'Expected both overlays to be tracked.'); - expect(keyboardDispatcher._attachedOverlays[0]).toBe(overlayOne, 'Expected one to be first.'); - expect(keyboardDispatcher._attachedOverlays[1]).toBe(overlayTwo, 'Expected two to be last.'); - - // Detach first one and re-attach it - keyboardDispatcher.remove(overlayOne); - keyboardDispatcher.add(overlayOne); - - expect(keyboardDispatcher._attachedOverlays[0]) - .toBe(overlayTwo, 'Expected two to now be first.'); - expect(keyboardDispatcher._attachedOverlays[1]) - .toBe(overlayOne, 'Expected one to now be last.'); - }); - - it('should dispatch body keyboard events to the most recently attached overlay', () => { - const overlayOne = overlay.create(); - const overlayTwo = overlay.create(); - const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy'); - const overlayTwoSpy = jasmine.createSpy('overlayOne keyboard event spy'); - - overlayOne.keydownEvents().subscribe(overlayOneSpy); - overlayTwo.keydownEvents().subscribe(overlayTwoSpy); - - // Attach overlays - keyboardDispatcher.add(overlayOne); - keyboardDispatcher.add(overlayTwo); - - dispatchKeyboardEvent(document.body, 'keydown', ESCAPE); - - // Most recent overlay should receive event - expect(overlayOneSpy).not.toHaveBeenCalled(); - expect(overlayTwoSpy).toHaveBeenCalled(); - }); - - it('should not dispatch keyboard events when propagation is stopped', () => { - const overlayRef = overlay.create(); - const spy = jasmine.createSpy('keyboard event spy'); - const button = document.createElement('button'); - - document.body.appendChild(button); - button.addEventListener('keydown', (event) => event.stopPropagation()); - - overlayRef.keydownEvents().subscribe(spy); - keyboardDispatcher.add(overlayRef); - dispatchKeyboardEvent(button, 'keydown', ESCAPE); - - expect(spy).not.toHaveBeenCalled(); - - button.parentNode!.removeChild(button); //tslint:disable-line - }); - - it('should complete the keydown stream on dispose', () => { - const overlayRef = overlay.create(); - const completeSpy = jasmine.createSpy('keydown complete spy'); - - overlayRef.keydownEvents().subscribe({complete: completeSpy}); - - overlayRef.dispose(); - - expect(completeSpy).toHaveBeenCalled(); - }); - - it('should stop emitting events to detached overlays', () => { - const instance = overlay.create(); - const spy = jasmine.createSpy('keyboard event spy'); - - instance.attach(new ComponentPortal(TestComponent)); - instance.keydownEvents().subscribe(spy); - - dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); - expect(spy).toHaveBeenCalledTimes(1); - - instance.detach(); - dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('should stop emitting events to disposed overlays', () => { - const instance = overlay.create(); - const spy = jasmine.createSpy('keyboard event spy'); - - instance.attach(new ComponentPortal(TestComponent)); - instance.keydownEvents().subscribe(spy); - - dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); - expect(spy).toHaveBeenCalledTimes(1); - - instance.dispose(); - dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('should dispose of the global keyboard event handler correctly', () => { - const overlayRef = overlay.create(); - const body = document.body; - - spyOn(body, 'addEventListener'); - spyOn(body, 'removeEventListener'); - - keyboardDispatcher.add(overlayRef); - expect(body.addEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function)); - - overlayRef.dispose(); - expect(body.removeEventListener).toHaveBeenCalledWith('keydown', jasmine.any(Function)); - }); - -}); - - -@Component({ - template: 'Hello' -}) -class TestComponent { -} - - -// Create a real (non-test) NgModule as a workaround for -// https://github.com/angular/angular/issues/10760 -@NgModule({ - exports: [TestComponent], - declarations: [TestComponent], - entryComponents: [TestComponent] -}) -class TestComponentModule { -} diff --git a/packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts b/packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts deleted file mode 100644 index f4b7de909..000000000 --- a/packages/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { DOCUMENT } from '@angular/common'; -import { - Inject, - Injectable, - InjectionToken, - OnDestroy, - Optional, - SkipSelf -} from '@angular/core'; - -import { OverlayRef } from '../overlay-ref'; - - -/** - * Service for dispatching keyboard events that land on the body to appropriate overlay ref, - * if any. It maintains a list of attached overlays to determine best suited overlay based - * on event target and order of overlay opens. - */ -@Injectable({ providedIn: 'root' }) -export class OverlayKeyboardDispatcher implements OnDestroy { - - /** Currently attached overlays in the order they were attached. */ - _attachedOverlays: OverlayRef[] = []; - - private _document: Document; - private _isAttached: boolean; - - constructor(@Inject(DOCUMENT) document: any) { - this._document = document; - } - - ngOnDestroy() { - this._detach(); - } - - /** Add a new overlay to the list of attached overlay refs. */ - add(overlayRef: OverlayRef): void { - // Lazily start dispatcher once first overlay is added - if (!this._isAttached) { - this._document.body.addEventListener('keydown', this._keydownListener); - this._isAttached = true; - } - - this._attachedOverlays.push(overlayRef); - } - - /** Remove an overlay from the list of attached overlay refs. */ - remove(overlayRef: OverlayRef): void { - const index = this._attachedOverlays.indexOf(overlayRef); - - if (index > -1) { - this._attachedOverlays.splice(index, 1); - } - - // Remove the global listener once there are no more overlays. - if (this._attachedOverlays.length === 0) { - this._detach(); - } - } - - /** Detaches the global keyboard event listener. */ - private _detach() { - if (this._isAttached) { - this._document.body.removeEventListener('keydown', this._keydownListener); - this._isAttached = false; - } - } - - /** Keyboard event listener that will be attached to the body. */ - private _keydownListener = (event: KeyboardEvent) => { - if (this._attachedOverlays.length) { - // Dispatch the keydown event to the top overlay. We want to target the most recent overlay, - // rather than trying to match where the event came from, because some components might open - // an overlay, but keep focus on a trigger element (e.g. for select and autocomplete). - this._attachedOverlays[this._attachedOverlays.length - 1]._keydownEvents.next(event); - } - } -} - - -/** @docs-private @deprecated @deletion-target 7.0.0 */ -export function OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY( - dispatcher: OverlayKeyboardDispatcher, _document: any) { - return dispatcher || new OverlayKeyboardDispatcher(_document); -} - -/** @docs-private @deprecated @deletion-target 7.0.0 */ -export const OVERLAY_KEYBOARD_DISPATCHER_PROVIDER = { - // If there is already an OverlayKeyboardDispatcher available, use that. - // Otherwise, provide a new one. - provide: OverlayKeyboardDispatcher, - deps: [ - [new Optional(), new SkipSelf(), OverlayKeyboardDispatcher], - - // Coerce to `InjectionToken` so that the `deps` match the "shape" - // of the type expected by Angular - DOCUMENT as InjectionToken - ], - useFactory: OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY -}; diff --git a/packages/cdk/overlay/overlay-config.ts b/packages/cdk/overlay/overlay-config.ts deleted file mode 100644 index bcbad524b..000000000 --- a/packages/cdk/overlay/overlay-config.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; - -import { IPositionStrategy } from './position/position-strategy'; -import { NoopScrollStrategy } from './scroll/noop-scroll-strategy'; -import { IScrollStrategy } from './scroll/scroll-strategy'; - - -/** Initial configuration used when creating an overlay. */ -export class OverlayConfig { - /** Strategy with which to position the overlay. */ - positionStrategy?: IPositionStrategy; - - /** Strategy to be used when handling scroll events while the overlay is open. */ - scrollStrategy?: IScrollStrategy = new NoopScrollStrategy(); - - /** Custom class to add to the overlay pane. */ - panelClass?: string | string[] = ''; - - /** Whether the overlay has a backdrop. */ - hasBackdrop?: boolean = false; - - /** Custom class to add to the backdrop */ - backdropClass?: string | string[] = 'cdk-overlay-dark-backdrop'; - - /** The width of the overlay panel. If a number is provided, pixel units are assumed. */ - width?: number | string; - - /** The height of the overlay panel. If a number is provided, pixel units are assumed. */ - height?: number | string; - - /** The min-width of the overlay panel. If a number is provided, pixel units are assumed. */ - minWidth?: number | string; - - /** The min-height of the overlay panel. If a number is provided, pixel units are assumed. */ - minHeight?: number | string; - - /** The max-width of the overlay panel. If a number is provided, pixel units are assumed. */ - maxWidth?: number | string; - - /** The max-height of the overlay panel. If a number is provided, pixel units are assumed. */ - maxHeight?: number | string; - - /** - * Direction of the text in the overlay panel. If a `Directionality` instance - * is passed in, the overlay will handle changes to its value automatically. - */ - direction?: Direction | Directionality; - - constructor(config?: OverlayConfig) { - if (config) { - Object.keys(config) - .filter((key) => config[key] !== undefined) - .forEach((key) => this[key] = config[key]); - } - } -} diff --git a/packages/cdk/overlay/overlay-container.spec.ts b/packages/cdk/overlay/overlay-container.spec.ts deleted file mode 100644 index a8d31cbc2..000000000 --- a/packages/cdk/overlay/overlay-container.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Component, NgModule, ViewChild, ViewContainerRef } from '@angular/core'; -import { async, inject, TestBed } from '@angular/core/testing'; -import { PortalModule, CdkPortal } from '@ptsecurity/cdk/portal'; - -import { Overlay, OverlayContainer, OverlayModule } from './index'; - - -describe('OverlayContainer', () => { - let overlay: Overlay; - let overlayContainer: OverlayContainer; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [OverlayTestModule] - }).compileComponents(); - })); - - beforeEach(inject([Overlay, OverlayContainer], (o: Overlay, oc: OverlayContainer) => { - overlay = o; - overlayContainer = oc; - })); - - afterEach(() => { - overlayContainer.ngOnDestroy(); - }); - - it('should remove the overlay container element from the DOM on destruction', () => { - const fixture = TestBed.createComponent(TestComponentWithTemplatePortals); - - const overlayRef = overlay.create(); - overlayRef.attach(fixture.componentInstance.templatePortal); - fixture.detectChanges(); - - expect(document.querySelector('.cdk-overlay-container')) - .not.toBeNull('Expected the overlay container to be in the DOM after opening an overlay'); - - // Manually call `ngOnDestroy` because there is no way to force Angular to destroy an - // injectable in a unit test. - overlayContainer.ngOnDestroy(); - - expect(document.querySelector('.cdk-overlay-container')) - .toBeNull('Expected the overlay container *not* to be in the DOM after destruction'); - }); - - it('should add and remove css classes from the container element', () => { - overlayContainer.getContainerElement().classList.add('commander-shepard'); - - const containerElement = document.querySelector('.cdk-overlay-container')!; // tslint:disable-line - expect(containerElement.classList.contains('commander-shepard')) - .toBe(true, 'Expected the overlay container to have class "commander-shepard"'); - - overlayContainer.getContainerElement().classList.remove('commander-shepard'); - - expect(containerElement.classList.contains('commander-shepard')) - .toBe(false, 'Expected the overlay container not to have class "commander-shepard"'); - }); -}); - -/** Test-bed component that contains a TempatePortal and an ElementRef. */ -@Component({ - template: ` - Cake`, - providers: [Overlay] -}) -class TestComponentWithTemplatePortals { - @ViewChild(CdkPortal, {static: true}) templatePortal: CdkPortal; - - constructor(public viewContainerRef: ViewContainerRef) { - } -} - -@NgModule({ - imports: [OverlayModule, PortalModule], - declarations: [TestComponentWithTemplatePortals] -}) -class OverlayTestModule { -} diff --git a/packages/cdk/overlay/overlay-container.ts b/packages/cdk/overlay/overlay-container.ts deleted file mode 100644 index 708245e89..000000000 --- a/packages/cdk/overlay/overlay-container.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { DOCUMENT } from '@angular/common'; -import { - Inject, - Injectable, - InjectionToken, - OnDestroy, - Optional, - SkipSelf -} from '@angular/core'; - - -/** Container inside which all overlays will render. */ -@Injectable({ providedIn: 'root' }) -export class OverlayContainer implements OnDestroy { - protected _containerElement: HTMLElement; - - constructor(@Inject(DOCUMENT) protected _document: any) { - } - - ngOnDestroy() { - if (this._containerElement && this._containerElement.parentNode) { - this._containerElement.parentNode.removeChild(this._containerElement); - } - } - - /** - * This method returns the overlay container element. It will lazily - * create the element the first time it is called to facilitate using - * the container in non-browser environments. - * @returns the container element - */ - getContainerElement(): HTMLElement { - if (!this._containerElement) { - this._createContainer(); - } - - return this._containerElement; - } - - /** - * Create the overlay container element, which is simply a div - * with the 'cdk-overlay-container' class on the document body. - */ - protected _createContainer(): void { - const container = this._document.createElement('div'); - - container.classList.add('cdk-overlay-container'); - this._document.body.appendChild(container); - this._containerElement = container; - } -} - - -/** @docs-private @deprecated @deletion-target 7.0.0 */ -export function OVERLAY_CONTAINER_PROVIDER_FACTORY(parentContainer: OverlayContainer, - _document: any) { - return parentContainer || new OverlayContainer(_document); -} - -/** @docs-private @deprecated @deletion-target 7.0.0 */ -export const OVERLAY_CONTAINER_PROVIDER = { - // If there is already an OverlayContainer available, use that. Otherwise, provide a new one. - provide: OverlayContainer, - deps: [ - [new Optional(), new SkipSelf(), OverlayContainer], - DOCUMENT as InjectionToken // We need to use the InjectionToken somewhere to keep TS happy - ], - useFactory: OVERLAY_CONTAINER_PROVIDER_FACTORY -}; diff --git a/packages/cdk/overlay/overlay-directives.spec.ts b/packages/cdk/overlay/overlay-directives.spec.ts deleted file mode 100644 index ff6ef52e5..000000000 --- a/packages/cdk/overlay/overlay-directives.spec.ts +++ /dev/null @@ -1,433 +0,0 @@ -import { Component, ViewChild } from '@angular/core'; -import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { dispatchKeyboardEvent } from '@ptsecurity/cdk/testing'; - -import { CdkConnectedOverlay, OverlayModule, CdkOverlayOrigin } from './index'; -import { OverlayContainer } from './overlay-container'; -import { - ConnectedOverlayPositionChange, - ConnectionPositionPair -} from './position/connected-position'; -import { FlexibleConnectedPositionStrategy } from './position/flexible-connected-position-strategy'; - - -describe('Overlay directives', () => { - let overlayContainer: OverlayContainer; - let overlayContainerElement: HTMLElement; - let fixture: ComponentFixture; - let dir: { value: string }; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [OverlayModule], - declarations: [ConnectedOverlayDirectiveTest, ConnectedOverlayPropertyInitOrder], - providers: [{ provide: Directionality, useFactory: () => dir = { value: 'ltr' } }] - }); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(ConnectedOverlayDirectiveTest); - fixture.detectChanges(); - }); - - beforeEach(inject([OverlayContainer], (oc: OverlayContainer) => { - overlayContainer = oc; - overlayContainerElement = oc.getContainerElement(); - })); - - afterEach(() => { - overlayContainer.ngOnDestroy(); - }); - - /** Returns the current open overlay pane element. */ - function getPaneElement() { - return overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - } - - it(`should attach the overlay based on the open property`, () => { - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(overlayContainerElement.textContent).toContain('Menu content'); - expect(getPaneElement().style.pointerEvents) - .toBe('auto', 'Expected the overlay pane to enable pointerEvents when attached.'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - expect(overlayContainerElement.textContent).toBe(''); - expect(getPaneElement().style.pointerEvents) - .toBe('none', 'Expected the overlay pane to disable pointerEvents when detached.'); - }); - - it('should destroy the overlay when the directive is destroyed', () => { - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - fixture.destroy(); - - expect(overlayContainerElement.textContent!.trim()).toBe(''); //tslint:disable-line - expect(getPaneElement()) - .toBeFalsy('Expected the overlay pane element to be removed when disposed.'); - }); - - it('should use a connected position strategy with a default set of positions', () => { - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - let testComponent: ConnectedOverlayDirectiveTest = //tslint:disable-line - fixture.debugElement.componentInstance; - let overlayDirective = testComponent.connectedOverlayDirective; //tslint:disable-line - let strategy = //tslint:disable-line - overlayDirective.overlayRef.getConfig().positionStrategy as FlexibleConnectedPositionStrategy; - - expect(strategy instanceof FlexibleConnectedPositionStrategy).toBe(true); - expect(strategy.positions.length).toBeGreaterThan(0); - }); - - it('should set and update the `dir` attribute', () => { - dir.value = 'rtl'; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - let boundingBox = - overlayContainerElement.querySelector('.cdk-overlay-connected-position-bounding-box')!; //tslint:disable-line - - expect(boundingBox.getAttribute('dir')).toBe('rtl'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - dir.value = 'ltr'; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - boundingBox = - overlayContainerElement.querySelector('.cdk-overlay-connected-position-bounding-box')!; //tslint:disable-line - - expect(boundingBox.getAttribute('dir')).toBe('ltr'); - }); - - it('should close when pressing escape', () => { - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - dispatchKeyboardEvent(document.body, 'keydown', ESCAPE); - fixture.detectChanges(); - - expect(overlayContainerElement.textContent!.trim()).toBe('', //tslint:disable-line - 'Expected overlay to have been detached.'); - }); - - it('should not depend on the order in which the `origin` and `open` are set', async(() => { - fixture.destroy(); - - const propOrderFixture = TestBed.createComponent(ConnectedOverlayPropertyInitOrder); - propOrderFixture.detectChanges(); - - const overlayDirective = propOrderFixture.componentInstance.connectedOverlayDirective; - - expect(() => { - overlayDirective.open = true; - overlayDirective.origin = propOrderFixture.componentInstance.trigger; - propOrderFixture.detectChanges(); - }).not.toThrow(); - })); - - describe('inputs', () => { - - it('should set the width', () => { - fixture.componentInstance.width = 250; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - expect(pane.style.width).toEqual('250px'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - fixture.componentInstance.width = 500; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(pane.style.width).toEqual('500px'); - }); - - it('should set the height', () => { - fixture.componentInstance.height = '100vh'; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - expect(pane.style.height).toEqual('100vh'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - fixture.componentInstance.height = '50vh'; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(pane.style.height).toEqual('50vh'); - }); - - it('should set the min width', () => { - fixture.componentInstance.minWidth = 250; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - expect(pane.style.minWidth).toEqual('250px'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - fixture.componentInstance.minWidth = 500; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(pane.style.minWidth).toEqual('500px'); - }); - - it('should set the min height', () => { - fixture.componentInstance.minHeight = '500px'; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - expect(pane.style.minHeight).toEqual('500px'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - fixture.componentInstance.minHeight = '250px'; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(pane.style.minHeight).toEqual('250px'); - }); - - it('should create the backdrop if designated', () => { - fixture.componentInstance.hasBackdrop = true; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); //tslint:disable-line - expect(backdrop).toBeTruthy(); - }); - - it('should not create the backdrop by default', () => { - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); //tslint:disable-line - expect(backdrop).toBeNull(); - }); - - it('should set the custom backdrop class', () => { - fixture.componentInstance.hasBackdrop = true; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const backdrop = - overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - expect(backdrop.classList).toContain('mc-test-class'); - }); - - it('should set the offsetX', () => { - fixture.componentInstance.offsetX = 5; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - - expect(pane.style.transform).toContain('translateX(5px)'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - fixture.componentInstance.offsetX = 15; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(pane.style.transform).toContain('translateX(15px)'); - }); - - it('should set the offsetY', () => { - const trigger = fixture.debugElement.query(By.css('button')).nativeElement; - trigger.style.position = 'absolute'; - trigger.style.top = '30px'; - trigger.style.height = '20px'; - - fixture.componentInstance.offsetY = 45; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - - expect(pane.style.transform).toContain('translateY(45px)'); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - - fixture.componentInstance.offsetY = 55; //tslint:disable-line - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - expect(pane.style.transform).toContain('translateY(55px)'); - }); - - it('should be able to set the viewport margin', () => { - expect(fixture.componentInstance.connectedOverlayDirective.viewportMargin).not.toBe(10); //tslint:disable-line - - fixture.componentInstance.viewportMargin = 10; //tslint:disable-line - fixture.detectChanges(); - - expect(fixture.componentInstance.connectedOverlayDirective.viewportMargin).toBe(10); //tslint:disable-line - }); - - it('should allow for flexible positioning to be enabled', () => { - expect(fixture.componentInstance.connectedOverlayDirective.flexibleDiemsions).not.toBe(true); - - fixture.componentInstance.flexibleDimensions = true; - fixture.detectChanges(); - - expect(fixture.componentInstance.connectedOverlayDirective.flexibleDiemsions).toBe(true); - }); - - it('should allow for growing after open to be enabled', () => { - expect(fixture.componentInstance.connectedOverlayDirective.growAfterOpen).not.toBe(true); - - fixture.componentInstance.growAfterOpen = true; - fixture.detectChanges(); - - expect(fixture.componentInstance.connectedOverlayDirective.growAfterOpen).toBe(true); - }); - - it('should allow for pushing to be enabled', () => { - expect(fixture.componentInstance.connectedOverlayDirective.push).not.toBe(true); - - fixture.componentInstance.push = true; - fixture.detectChanges(); - - expect(fixture.componentInstance.connectedOverlayDirective.push).toBe(true); - }); - - }); - - describe('outputs', () => { - it('should emit backdropClick appropriately', () => { - fixture.componentInstance.hasBackdrop = true; - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - const backdrop = - overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - backdrop.click(); - fixture.detectChanges(); - - expect(fixture.componentInstance.backdropClickHandler) - .toHaveBeenCalledWith(jasmine.any(MouseEvent)); - }); - - it('should emit positionChange appropriately', () => { - expect(fixture.componentInstance.positionChangeHandler).not.toHaveBeenCalled(); - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(fixture.componentInstance.positionChangeHandler).toHaveBeenCalled(); - - const latestCall = fixture.componentInstance.positionChangeHandler.calls.mostRecent(); - - expect(latestCall.args[0] instanceof ConnectedOverlayPositionChange) - .toBe(true, `Expected directive to emit an instance of ConnectedOverlayPositionChange.`); - }); - - it('should emit attach and detach appropriately', () => { - expect(fixture.componentInstance.attachHandler).not.toHaveBeenCalled(); - expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled(); - fixture.componentInstance.isOpen = true; - fixture.detectChanges(); - - expect(fixture.componentInstance.attachHandler).toHaveBeenCalled(); - expect(fixture.componentInstance.attachResult instanceof HTMLElement) - .toBe(true, `Expected pane to be populated with HTML elements when attach was called.`); - expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled(); - - fixture.componentInstance.isOpen = false; - fixture.detectChanges(); - expect(fixture.componentInstance.detachHandler).toHaveBeenCalled(); - }); - - }); - -}); - - -@Component({ - template: ` - - - -

Menu content

-
` -}) -class ConnectedOverlayDirectiveTest { - @ViewChild(CdkConnectedOverlay, {static: false}) connectedOverlayDirective: CdkConnectedOverlay; - @ViewChild('trigger', {static: false}) trigger: CdkOverlayOrigin; - @ViewChild('otherTrigger', {static: false}) otherTrigger: CdkOverlayOrigin; - - isOpen = false; - width: number | string; - height: number | string; - minWidth: number | string; - minHeight: number | string; - offsetX: number; - offsetY: number; - triggerOverride: CdkOverlayOrigin; - hasBackdrop: boolean; - viewportMargin: number; - flexibleDimensions: boolean; - growAfterOpen: boolean; - push: boolean; - backdropClickHandler = jasmine.createSpy('backdropClick handler'); - positionChangeHandler = jasmine.createSpy('positionChangeHandler'); - positionOverrides: ConnectionPositionPair[]; - attachHandler = jasmine.createSpy('attachHandler').and.callFake(() => { - this.attachResult = - this.connectedOverlayDirective.overlayRef.overlayElement.querySelector('p') as HTMLElement; - }); - detachHandler = jasmine.createSpy('detachHandler'); - attachResult: HTMLElement; -} - -@Component({ - template: ` - - Menu content` -}) -class ConnectedOverlayPropertyInitOrder { - @ViewChild(CdkConnectedOverlay, {static: false}) connectedOverlayDirective: CdkConnectedOverlay; - @ViewChild('trigger', {static: false}) trigger: CdkOverlayOrigin; -} diff --git a/packages/cdk/overlay/overlay-directives.ts b/packages/cdk/overlay/overlay-directives.ts deleted file mode 100644 index bcc348db4..000000000 --- a/packages/cdk/overlay/overlay-directives.ts +++ /dev/null @@ -1,420 +0,0 @@ -import { - Directive, - ElementRef, - EventEmitter, - Inject, - InjectionToken, - Input, - OnChanges, - OnDestroy, - Optional, - Output, - SimpleChanges, - TemplateRef, - ViewContainerRef -} from '@angular/core'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; -import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { TemplatePortal } from '@ptsecurity/cdk/portal'; -import { Subscription } from 'rxjs'; - -import { Overlay } from './overlay'; -import { OverlayConfig } from './overlay-config'; -import { OverlayRef } from './overlay-ref'; -import { ConnectedOverlayPositionChange } from './position/connected-position'; -import { - IConnectedPosition, - FlexibleConnectedPositionStrategy -} from './position/flexible-connected-position-strategy'; -import { - RepositionScrollStrategy, - IRepositionScrollStrategyConfig, - IScrollStrategy -} from './scroll/index'; - - -/** Default set of positions for the overlay. Follows the behavior of a dropdown. */ -const defaultPositionList: IConnectedPosition[] = [ - { - originX: 'start', - originY: 'bottom', - overlayX: 'start', - overlayY: 'top' - }, - { - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'bottom' - }, - { - originX: 'end', - originY: 'top', - overlayX: 'end', - overlayY: 'bottom' - }, - { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - } -]; - -/** Injection token that determines the scroll handling while the connected overlay is open. */ -export const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY = - new InjectionToken<() => IScrollStrategy>('cdk-connected-overlay-scroll-strategy'); - -/** @docs-private @deprecated @deletion-target 7.0.0 */ -export function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_FACTORY(overlay: Overlay): - () => IScrollStrategy { - return (config?: IRepositionScrollStrategyConfig) => overlay.scrollStrategies.reposition(config); -} - -/** - * Directive applied to an element to make it usable as an origin for an Overlay using a - * ConnectedPositionStrategy. - */ -@Directive({ - selector: '[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]', - exportAs: 'cdkOverlayOrigin' -}) -export class CdkOverlayOrigin { - constructor( - /** Reference to the element on which the directive is applied. */ - public elementRef: ElementRef) { - } -} - - -/** - * Directive to facilitate declarative creation of an - * Overlay using a FlexibleConnectedPositionStrategy. - */ -@Directive({ - selector: '[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]', - exportAs: 'cdkConnectedOverlay' -}) -export class CdkConnectedOverlay implements OnDestroy, OnChanges { - /** Origin for the connected overlay. */ - @Input('cdkConnectedOverlayOrigin') origin: CdkOverlayOrigin; - - /** Registered connected position pairs. */ - @Input('cdkConnectedOverlayPositions') positions: IConnectedPosition[]; - - /** The offset in pixels for the overlay connection point on the x-axis */ - @Input('cdkConnectedOverlayOffsetX') - get offsetX(): number { - return this._offsetX; - } - - set offsetX(offsetX: number) { - this._offsetX = offsetX; - - if (this._position) { - this._setPositions(this._position); - } - } - - /** The offset in pixels for the overlay connection point on the y-axis */ - @Input('cdkConnectedOverlayOffsetY') - get offsetY() { - return this._offsetY; - } - - set offsetY(offsetY: number) { - this._offsetY = offsetY; - - if (this._position) { - this._setPositions(this._position); - } - } - - /** The width of the overlay panel. */ - @Input('cdkConnectedOverlayWidth') width: number | string; - - /** The height of the overlay panel. */ - @Input('cdkConnectedOverlayHeight') height: number | string; - - /** The min width of the overlay panel. */ - @Input('cdkConnectedOverlayMinWidth') minWidth: number | string; - - /** The min height of the overlay panel. */ - @Input('cdkConnectedOverlayMinHeight') minHeight: number | string; - - /** The custom class to be set on the backdrop element. */ - @Input('cdkConnectedOverlayBackdropClass') backdropClass: string; - - /** Margin between the overlay and the viewport edges. */ - @Input('cdkConnectedOverlayViewportMargin') viewportMargin: number = 0; - - /** Strategy to be used when handling scroll events while the overlay is open. */ - @Input('cdkConnectedOverlayScrollStrategy') scrollStrategy: IScrollStrategy = - this._scrollStrategy(); - - /** Whether the overlay is open. */ - @Input('cdkConnectedOverlayOpen') open: boolean = false; - - /** Whether or not the overlay should attach a backdrop. */ - @Input('cdkConnectedOverlayHasBackdrop') - get hasBackdrop() { - return this._hasBackdrop; - } - - set hasBackdrop(value: any) { - this._hasBackdrop = coerceBooleanProperty(value); - } - - /** Whether or not the overlay should be locked when scrolling. */ - @Input('cdkConnectedOverlayLockPosition') - get lockPosition() { - return this._lockPosition; - } - - set lockPosition(value: any) { - this._lockPosition = coerceBooleanProperty(value); - } - - /** Whether the overlay's width and height can be constrained to fit within the viewport. */ - @Input('cdkConnectedOverlayFlexibleDimensions') - get flexibleDiemsions() { - return this._flexibleDimensions; - } - - set flexibleDiemsions(value: boolean) { - this._flexibleDimensions = coerceBooleanProperty(value); - } - - /** Whether the overlay can grow after the initial open when flexible positioning is turned on. */ - @Input('cdkConnectedOverlayGrowAfterOpen') - get growAfterOpen() { - return this._growAfterOpen; - } - - set growAfterOpen(value: boolean) { - this._growAfterOpen = coerceBooleanProperty(value); - } - - /** Whether the overlay can be pushed on-screen if none of the provided positions fit. */ - @Input('cdkConnectedOverlayPush') - get push() { - return this._push; - } - - set push(value: boolean) { - this._push = coerceBooleanProperty(value); - } - - /** Event emitted when the backdrop is clicked. */ - @Output() backdropClick = new EventEmitter(); - - /** Event emitted when the position has changed. */ - @Output() positionChange = new EventEmitter(); - - /** Event emitted when the overlay has been attached. */ - @Output() attach = new EventEmitter(); - - /** Event emitted when the overlay has been detached. */ - @Output() detach = new EventEmitter(); - - private _overlayRef: OverlayRef; - private _templatePortal: TemplatePortal; - private _hasBackdrop = false; - private _lockPosition = false; - private _growAfterOpen = false; - private _flexibleDimensions = false; - private _push = false; - private _backdropSubscription = Subscription.EMPTY; - private _offsetX: number; - private _offsetY: number; - private _position: FlexibleConnectedPositionStrategy; - - constructor( - private _overlay: Overlay, - templateRef: TemplateRef, - viewContainerRef: ViewContainerRef, - @Inject(CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy, - @Optional() private _dir: Directionality - ) { - this._templatePortal = new TemplatePortal(templateRef, viewContainerRef); - } - - /** The associated overlay reference. */ - get overlayRef(): OverlayRef { - return this._overlayRef; - } - - /** The element's layout direction. */ - get dir(): Direction { - return this._dir ? this._dir.value : 'ltr'; - } - - ngOnDestroy() { - this._destroyOverlay(); - } - - ngOnChanges(changes: SimpleChanges) { - if (this._position) { - if (changes['positions']) { //tslint:disable-line - this._position.withPositions(this.positions); - } - - if (changes['lockPosition']) { //tslint:disable-line - this._position.withLockedPosition(this.lockPosition); - } - - if (changes['origin']) { //tslint:disable-line - this._position.setOrigin(this.origin.elementRef); - - if (this.open) { - this._position.apply(); - } - } - } - - if (changes['open']) { //tslint:disable-line - this.open ? this._attachOverlay() : this._detachOverlay(); //tslint:disable-line - } - } - - /** Creates an overlay */ - private _createOverlay() { - if (!this.positions || !this.positions.length) { - this.positions = defaultPositionList; - } - - this._overlayRef = this._overlay.create(this._buildConfig()); - } - - /** Builds the overlay config based on the directive's inputs */ - private _buildConfig(): OverlayConfig { - const positionStrategy = this._position = this._createPositionStrategy(); - const overlayConfig = new OverlayConfig({ - direction: this._dir, - positionStrategy, - scrollStrategy: this.scrollStrategy, - hasBackdrop: this.hasBackdrop - }); - - if (this.width || this.width === 0) { - overlayConfig.width = this.width; - } - - if (this.height || this.height === 0) { - overlayConfig.height = this.height; - } - - if (this.minWidth || this.minWidth === 0) { - overlayConfig.minWidth = this.minWidth; - } - - if (this.minHeight || this.minHeight === 0) { - overlayConfig.minHeight = this.minHeight; - } - - if (this.backdropClass) { - overlayConfig.backdropClass = this.backdropClass; - } - - return overlayConfig; - } - - /** Returns the position strategy of the overlay to be set on the overlay config */ - private _createPositionStrategy(): FlexibleConnectedPositionStrategy { - const strategy = this._overlay.position() - .flexibleConnectedTo(this.origin.elementRef) - .withFlexibleDimensions(this.flexibleDiemsions) - .withPush(this.push) - .withGrowAfterOpen(this.growAfterOpen) - .withViewportMargin(this.viewportMargin) - .withLockedPosition(this.lockPosition); - - this._setPositions(strategy); - strategy.positionChanges.subscribe((position) => this.positionChange.emit(position)); - - return strategy; - } - - /** - * Sets the primary and fallback positions of a positions strategy, - * based on the current directive inputs. - */ - private _setPositions(positionStrategy: FlexibleConnectedPositionStrategy) { - const positions: IConnectedPosition[] = this.positions.map((pos) => ({ - originX: pos.originX, - originY: pos.originY, - overlayX: pos.overlayX, - overlayY: pos.overlayY, - offsetX: pos.offsetX || this.offsetX, - offsetY: pos.offsetY || this.offsetY - })); - - positionStrategy.withPositions(positions); - } - - /** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */ - private _attachOverlay() { - if (!this._overlayRef) { - this._createOverlay(); - - this._overlayRef!.keydownEvents().subscribe((event: KeyboardEvent) => { //tslint:disable-line - if (event.keyCode === ESCAPE) { //tslint:disable-line - this._detachOverlay(); - } - }); - } else { - // Update the overlay size, in case the directive's inputs have changed - this._overlayRef.updateSize({ - width: this.width, - minWidth: this.minWidth, - height: this.height, - minHeight: this.minHeight - }); - } - - if (!this._overlayRef.hasAttached()) { - this._overlayRef.attach(this._templatePortal); - this.attach.emit(); - } - - if (this.hasBackdrop) { - this._backdropSubscription = this._overlayRef.backdropClick().subscribe((event) => { - this.backdropClick.emit(event); - }); - } - } - - /** Detaches the overlay and unsubscribes to backdrop clicks if backdrop exists */ - private _detachOverlay() { - if (this._overlayRef) { - this._overlayRef.detach(); - this.detach.emit(); - } - - this._backdropSubscription.unsubscribe(); - } - - /** Destroys the overlay created by this directive. */ - private _destroyOverlay() { - if (this._overlayRef) { - this._overlayRef.dispose(); - } - - this._backdropSubscription.unsubscribe(); - } -} - - -/** @docs-private */ -export function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay): - () => RepositionScrollStrategy { - return () => overlay.scrollStrategies.reposition(); -} - -/** @docs-private */ -export const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { - provide: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY, - deps: [Overlay], - useFactory: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY -}; diff --git a/packages/cdk/overlay/overlay-module.ts b/packages/cdk/overlay/overlay-module.ts deleted file mode 100644 index 21688e976..000000000 --- a/packages/cdk/overlay/overlay-module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { NgModule, Provider } from '@angular/core'; -import { BidiModule } from '@ptsecurity/cdk/bidi'; -import { PortalModule } from '@ptsecurity/cdk/portal'; -import { ScrollDispatchModule, VIEWPORT_RULER_PROVIDER } from '@ptsecurity/cdk/scrolling'; - -import { OVERLAY_KEYBOARD_DISPATCHER_PROVIDER } from './keyboard/overlay-keyboard-dispatcher'; -import { Overlay } from './overlay'; -import { OVERLAY_CONTAINER_PROVIDER } from './overlay-container'; -import { - CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, - CdkConnectedOverlay, - CdkOverlayOrigin -} from './overlay-directives'; -import { OverlayPositionBuilder } from './position/overlay-position-builder'; - - -@NgModule({ - imports: [BidiModule, PortalModule, ScrollDispatchModule], - exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollDispatchModule], - declarations: [CdkConnectedOverlay, CdkOverlayOrigin], - providers: [ - Overlay, - CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER - ] -}) -export class OverlayModule { -} - - -/** - * @deprecated Use `OverlayModule` instead. - * @deletion-target 7.0.0 - */ -export const OVERLAY_PROVIDERS: Provider[] = [ - Overlay, - OverlayPositionBuilder, - OVERLAY_KEYBOARD_DISPATCHER_PROVIDER, - VIEWPORT_RULER_PROVIDER, - OVERLAY_CONTAINER_PROVIDER, - CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, -]; diff --git a/packages/cdk/overlay/overlay-prebuilt.scss b/packages/cdk/overlay/overlay-prebuilt.scss deleted file mode 100644 index 5c3798a31..000000000 --- a/packages/cdk/overlay/overlay-prebuilt.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './overlay'; - -@include cdk-overlay(); diff --git a/packages/cdk/overlay/overlay-ref.ts b/packages/cdk/overlay/overlay-ref.ts deleted file mode 100644 index 0ca1ea351..000000000 --- a/packages/cdk/overlay/overlay-ref.ts +++ /dev/null @@ -1,382 +0,0 @@ -import { ComponentRef, EmbeddedViewRef, NgZone } from '@angular/core'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceCssPixelValue, coerceArray } from '@ptsecurity/cdk/coercion'; -import { ComponentPortal, Portal, IPortalOutlet, TemplatePortal } from '@ptsecurity/cdk/portal'; -import { Observable, Subject } from 'rxjs'; -import { take } from 'rxjs/operators'; - -import { OverlayKeyboardDispatcher } from './keyboard/overlay-keyboard-dispatcher'; -import { OverlayConfig } from './overlay-config'; - - -/** An object where all of its properties cannot be written. */ -export type ImmutableObject = { - readonly [P in keyof T]: T[P]; -}; - -/** - * Reference to an overlay that has been created with the Overlay service. - * Used to manipulate or dispose of said overlay. - */ -export class OverlayRef implements IPortalOutlet { - /** Stream of keydown events dispatched to this overlay. */ - _keydownEvents = new Subject(); - - private _backdropElement: HTMLElement | null = null; - private _backdropClick: Subject = new Subject(); - private _attachments = new Subject(); - private _detachments = new Subject(); - - constructor( - private _portalOutlet: IPortalOutlet, - private _host: HTMLElement, - private _pane: HTMLElement, - private _config: ImmutableObject, - private _ngZone: NgZone, - private _keyboardDispatcher: OverlayKeyboardDispatcher, - private _document: Document) { - - if (_config.scrollStrategy) { - _config.scrollStrategy.attach(this); - } - } - - /** The overlay's HTML element */ - get overlayElement(): HTMLElement { - return this._pane; - } - - /** The overlay's backdrop HTML element. */ - get backdropElement(): HTMLElement | null { - return this._backdropElement; - } - - /** - * Wrapper around the panel element. Can be used for advanced - * positioning where a wrapper with specific styling is - * required around the overlay pane. - */ - get hostElement(): HTMLElement { - return this._host; - } - - attach(portal: ComponentPortal): ComponentRef; - - attach(portal: TemplatePortal): EmbeddedViewRef; - - attach(portal: any): any; - - /** - * Attaches content, given via a Portal, to the overlay. - * If the overlay is configured to have a backdrop, it will be created. - * - * @param portal Portal instance to which to attach the overlay. - * @returns The portal attachment result. - */ - attach(portal: Portal): any { - let attachResult = this._portalOutlet.attach(portal); //tslint:disable-line - - if (this._config.positionStrategy) { - this._config.positionStrategy.attach(this); - } - - // Update the pane element with the given configuration. - this._updateStackingOrder(); - this._updateElementSize(); - this._updateElementDirection(); - - if (this._config.scrollStrategy) { - this._config.scrollStrategy.enable(); - } - - // Update the position once the zone is stable so that the overlay will be fully rendered - // before attempting to position it, as the position may depend on the size of the rendered - // content. - this._ngZone.onStable - .asObservable() - .pipe(take(1)) - .subscribe(() => { - // The overlay could've been detached before the zone has stabilized. - if (this.hasAttached()) { - this.updatePosition(); - } - }); - - // Enable pointer events for the overlay pane element. - this._togglePointerEvents(true); - - if (this._config.hasBackdrop) { - this._attachBackdrop(); - } - - if (this._config.panelClass) { - this._toggleClasses(this._pane, this._config.panelClass, true); - } - - // Only emit the `attachments` event once all other setup is done. - this._attachments.next(); - - // Track this overlay by the keyboard dispatcher - this._keyboardDispatcher.add(this); - - return attachResult; - } - - /** - * Detaches an overlay from a portal. - * @returns The portal detachment result. - */ - detach(): any { - if (!this.hasAttached()) { - return; - } - - this.detachBackdrop(); - - // When the overlay is detached, the pane element should disable pointer events. - // This is necessary because otherwise the pane element will cover the page and disable - // pointer events therefore. Depends on the position strategy and the applied pane boundaries. - this._togglePointerEvents(false); - - if (this._config.positionStrategy && this._config.positionStrategy.detach) { - this._config.positionStrategy.detach(); - } - - if (this._config.scrollStrategy) { - this._config.scrollStrategy.disable(); - } - - const detachmentResult = this._portalOutlet.detach(); - - // Only emit after everything is detached. - this._detachments.next(); - - // Remove this overlay from keyboard dispatcher tracking - this._keyboardDispatcher.remove(this); - - return detachmentResult; - } - - /** Cleans up the overlay from the DOM. */ - dispose(): void { - const isAttached = this.hasAttached(); - - if (this._config.positionStrategy) { - this._config.positionStrategy.dispose(); - } - - if (this._config.scrollStrategy) { - this._config.scrollStrategy.disable(); - } - - this.detachBackdrop(); - this._keyboardDispatcher.remove(this); - this._portalOutlet.dispose(); - this._attachments.complete(); - this._backdropClick.complete(); - this._keydownEvents.complete(); - - if (this._host && this._host.parentNode) { - this._host.parentNode.removeChild(this._host); - this._host = null!; //tslint:disable-line - } - - this._pane = null!; //tslint:disable-line - - if (isAttached) { - this._detachments.next(); - } - - this._detachments.complete(); - } - - /** Whether the overlay has attached content. */ - hasAttached(): boolean { - return this._portalOutlet.hasAttached(); - } - - /** Gets an observable that emits when the backdrop has been clicked. */ - backdropClick(): Observable { - return this._backdropClick.asObservable(); - } - - /** Gets an observable that emits when the overlay has been attached. */ - attachments(): Observable { - return this._attachments.asObservable(); - } - - /** Gets an observable that emits when the overlay has been detached. */ - detachments(): Observable { - return this._detachments.asObservable(); - } - - /** Gets an observable of keydown events targeted to this overlay. */ - keydownEvents(): Observable { - return this._keydownEvents.asObservable(); - } - - /** Gets the the current overlay configuration, which is immutable. */ - getConfig(): OverlayConfig { - return this._config; - } - - /** Updates the position of the overlay based on the position strategy. */ - updatePosition() { - if (this._config.positionStrategy) { - this._config.positionStrategy.apply(); - } - } - - /** Update the size properties of the overlay. */ - updateSize(sizeConfig: IOverlaySizeConfig) { - this._config = { ...this._config, ...sizeConfig }; - this._updateElementSize(); - } - - /** Sets the LTR/RTL direction for the overlay. */ - setDirection(dir: Direction | Directionality) { - this._config = { ...this._config, direction: dir }; - this._updateElementDirection(); - } - - /** - * Returns the layout direction of the overlay panel. - */ - getDirection(): Direction { - const direction = this._config.direction; - - if (!direction) { - return 'ltr'; - } - - return typeof direction === 'string' ? direction : direction.value; - } - - /** Updates the text direction of the overlay panel. */ - private _updateElementDirection() { - this._host.setAttribute('dir', this.getDirection()); - } - - /** Updates the size of the overlay element based on the overlay config. */ - private _updateElementSize() { - const style = this._pane.style; - - style.width = coerceCssPixelValue(this._config.width); - style.height = coerceCssPixelValue(this._config.height); - style.minWidth = coerceCssPixelValue(this._config.minWidth); - style.minHeight = coerceCssPixelValue(this._config.minHeight); - style.maxWidth = coerceCssPixelValue(this._config.maxWidth); - style.maxHeight = coerceCssPixelValue(this._config.maxHeight); - } - - /** Toggles the pointer events for the overlay pane element. */ - private _togglePointerEvents(enablePointer: boolean) { - this._pane.style.pointerEvents = enablePointer ? 'auto' : 'none'; - } - - /** Attaches a backdrop for this overlay. */ - private _attachBackdrop() { - const showingClass = 'cdk-overlay-backdrop-showing'; - - this._backdropElement = this._document.createElement('div'); - this._backdropElement.classList.add('cdk-overlay-backdrop'); - - if (this._config.backdropClass) { - this._toggleClasses(this._backdropElement, this._config.backdropClass, true); - } - - // Insert the backdrop before the pane in the DOM order, - // in order to handle stacked overlays properly. - this._host.parentElement!.insertBefore(this._backdropElement, this._host); //tslint:disable-line - - // Forward backdrop clicks such that the consumer of the overlay can perform whatever - // action desired when such a click occurs (usually closing the overlay). - this._backdropElement.addEventListener('click', - (event: MouseEvent) => this._backdropClick.next(event)); - - // Add class to fade-in the backdrop after one frame. - if (requestAnimationFrame !== undefined) { - this._ngZone.runOutsideAngular(() => { - requestAnimationFrame(() => { - if (this._backdropElement) { - this._backdropElement.classList.add(showingClass); - } - }); - }); - } else { - this._backdropElement.classList.add(showingClass); - } - } - - /** - * Updates the stacking order of the element, moving it to the top if necessary. - * This is required in cases where one overlay was detached, while another one, - * that should be behind it, was destroyed. The next time both of them are opened, - * the stacking will be wrong, because the detached element's pane will still be - * in its original DOM position. - */ - private _updateStackingOrder() { - if (this._host.nextSibling) { - this._host.parentNode!.appendChild(this._host); //tslint:disable-line - } - } - - /** Detaches the backdrop (if any) associated with the overlay. */ - detachBackdrop(): void { - let backdropToDetach = this._backdropElement; //tslint:disable-line - - if (backdropToDetach) { - let finishDetach = () => { //tslint:disable-line - // It may not be attached to anything in certain cases (e.g. unit tests). - if (backdropToDetach && backdropToDetach.parentNode) { - backdropToDetach.parentNode.removeChild(backdropToDetach); - } - - // It is possible that a new portal has been attached to this overlay since we started - // removing the backdrop. If that is the case, only clear the backdrop reference if it - // is still the same instance that we started to remove. - if (this._backdropElement === backdropToDetach) { - this._backdropElement = null; - } - }; - - backdropToDetach.classList.remove('cdk-overlay-backdrop-showing'); - - if (this._config.backdropClass) { - this._toggleClasses(backdropToDetach, this._config.backdropClass, false); - } - - backdropToDetach.addEventListener('transitionend', finishDetach); - - // If the backdrop doesn't have a transition, the `transitionend` event won't fire. - // In this case we make it unclickable and we try to remove it after a delay. - backdropToDetach.style.pointerEvents = 'none'; - - // Run this outside the Angular zone because there's nothing that Angular cares about. - // If it were to run inside the Angular zone, every test that used Overlay would have to be - // either async or fakeAsync. - this._ngZone.runOutsideAngular(() => setTimeout(finishDetach, 500)); //tslint:disable-line - } - } - - /** Toggles a single CSS class or an array of classes on an element. */ - private _toggleClasses(element: HTMLElement, cssClasses: string | string[], isAdd: boolean) { - const classList = element.classList; - - coerceArray(cssClasses).forEach((cssClass) => { - // We can't do a spread here, because IE doesn't support setting multiple classes. - isAdd ? classList.add(cssClass) : classList.remove(cssClass); // tslint:disable-line - }); - } -} - - -/** Size properties for an overlay. */ -export interface IOverlaySizeConfig { - width?: number | string; - height?: number | string; - minWidth?: number | string; - minHeight?: number | string; - maxWidth?: number | string; - maxHeight?: number | string; -} diff --git a/packages/cdk/overlay/overlay-reference.ts b/packages/cdk/overlay/overlay-reference.ts deleted file mode 100644 index b536f374a..000000000 --- a/packages/cdk/overlay/overlay-reference.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; -import { Portal } from '@ptsecurity/cdk/portal'; - - -/** - * Basic interface for an overlay. Used to avoid circular type references between - * `OverlayRef`, `PositionStrategy` and `ScrollStrategy`, and `OverlayConfig`. - * @docs-private - */ -export interface IOverlayReference { - overlayElement: HTMLElement; - hostElement: HTMLElement; - - attach(portal: Portal): any; - - detach(): any; - - dispose(): void; - - getConfig(): any; - - hasAttached(): boolean; - - updateSize(config: any): void; - - updatePosition(): void; - - getDirection(): Direction; - - setDirection(dir: Direction | Directionality): void; -} diff --git a/packages/cdk/overlay/overlay.spec.ts b/packages/cdk/overlay/overlay.spec.ts deleted file mode 100644 index f31ca4b83..000000000 --- a/packages/cdk/overlay/overlay.spec.ts +++ /dev/null @@ -1,690 +0,0 @@ -import { - Component, - NgModule, - ViewChild, - ViewContainerRef, - ErrorHandler, - Injectable, - EventEmitter -} from '@angular/core'; -import { async, fakeAsync, tick, ComponentFixture, inject, TestBed } from '@angular/core/testing'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; -import { - ComponentPortal, - PortalModule, - TemplatePortal, - CdkPortal -} from '@ptsecurity/cdk/portal'; - -import { - Overlay, - OverlayContainer, - OverlayModule, - OverlayRef, - OverlayConfig, - IPositionStrategy, - IScrollStrategy -} from './index'; - - -describe('Overlay', () => { - let overlay: Overlay; - let componentPortal: ComponentPortal; - let templatePortal: TemplatePortal; - let overlayContainerElement: HTMLElement; - let overlayContainer: OverlayContainer; - let viewContainerFixture: ComponentFixture; - let dir: Direction; - - beforeEach(async(() => { - dir = 'ltr'; - TestBed.configureTestingModule({ - imports: [OverlayModule, PortalModule, OverlayTestModule], - providers: [{ - provide: Directionality, - useFactory: () => { - const fakeDirectionality = {}; - Object.defineProperty(fakeDirectionality, 'value', { get: () => dir }); - - return fakeDirectionality; - } - }] - }).compileComponents(); - })); - - beforeEach(inject([Overlay, OverlayContainer], (o: Overlay, oc: OverlayContainer) => { - overlay = o; - overlayContainer = oc; - overlayContainerElement = oc.getContainerElement(); - - let fixture = TestBed.createComponent(TestComponentWithTemplatePortals); // tslint:disable-line - fixture.detectChanges(); - templatePortal = fixture.componentInstance.templatePortal; - componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef); - viewContainerFixture = fixture; - })); - - afterEach(() => { - overlayContainer.ngOnDestroy(); - }); - - it('should load a component into an overlay', () => { - const overlayRef = overlay.create(); - overlayRef.attach(componentPortal); - - expect(overlayContainerElement.textContent).toContain('Pizza'); - - overlayRef.dispose(); - expect(overlayContainerElement.childNodes.length).toBe(0); - expect(overlayContainerElement.textContent).toBe(''); - }); - - it('should load a template portal into an overlay', () => { - const overlayRef = overlay.create(); - overlayRef.attach(templatePortal); - - expect(overlayContainerElement.textContent).toContain('Cake'); - - overlayRef.dispose(); - expect(overlayContainerElement.childNodes.length).toBe(0); - expect(overlayContainerElement.textContent).toBe(''); - }); - - it('should disable pointer events of the pane element if detached', () => { - const overlayRef = overlay.create(); - const paneElement = overlayRef.overlayElement; - - overlayRef.attach(componentPortal); - viewContainerFixture.detectChanges(); - - expect(paneElement.childNodes.length).not.toBe(0); - expect(paneElement.style.pointerEvents) - .toBe('auto', 'Expected the overlay pane to enable pointerEvents when attached.'); - - overlayRef.detach(); - - expect(paneElement.childNodes.length).toBe(0); - expect(paneElement.style.pointerEvents) - .toBe('none', 'Expected the overlay pane to disable pointerEvents when detached.'); - }); - - it('should open multiple overlays', () => { - const pizzaOverlayRef = overlay.create(); - pizzaOverlayRef.attach(componentPortal); - - const cakeOverlayRef = overlay.create(); - cakeOverlayRef.attach(templatePortal); - - expect(overlayContainerElement.childNodes.length).toBe(2); // tslint:disable-line - expect(overlayContainerElement.textContent).toContain('Pizza'); - expect(overlayContainerElement.textContent).toContain('Cake'); - - pizzaOverlayRef.dispose(); - expect(overlayContainerElement.childNodes.length).toBe(1); - expect(overlayContainerElement.textContent).toContain('Cake'); - - cakeOverlayRef.dispose(); - expect(overlayContainerElement.childNodes.length).toBe(0); - expect(overlayContainerElement.textContent).toBe(''); - }); - - it('should ensure that the most-recently-attached overlay is on top', (() => { - let pizzaOverlayRef = overlay.create(); - const cakeOverlayRef = overlay.create(); - - pizzaOverlayRef.attach(componentPortal); - cakeOverlayRef.attach(templatePortal); - - expect(pizzaOverlayRef.hostElement.nextSibling) - .toBeTruthy('Expected pizza to be on the bottom.'); - expect(cakeOverlayRef.hostElement.nextSibling) - .toBeFalsy('Expected cake to be on top.'); - - pizzaOverlayRef.dispose(); - cakeOverlayRef.detach(); - - pizzaOverlayRef = overlay.create(); - pizzaOverlayRef.attach(componentPortal); - cakeOverlayRef.attach(templatePortal); - - expect(pizzaOverlayRef.hostElement.nextSibling) - .toBeTruthy('Expected pizza to still be on the bottom.'); - expect(cakeOverlayRef.hostElement.nextSibling) - .toBeFalsy('Expected cake to still be on top.'); - })); - - it('should take the default direction from the global Directionality', () => { - dir = 'rtl'; - const overlayRef = overlay.create(); - - overlayRef.attach(componentPortal); - expect(overlayRef.hostElement.getAttribute('dir')).toBe('rtl'); - }); - - it('should set the direction', () => { - const config = new OverlayConfig({ direction: 'rtl' }); - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - - expect(overlayRef.hostElement.getAttribute('dir')).toEqual('rtl'); - }); - - it('should emit when an overlay is attached', () => { - const overlayRef = overlay.create(); - const spy = jasmine.createSpy('attachments spy'); - - overlayRef.attachments().subscribe(spy); - overlayRef.attach(componentPortal); - - expect(spy).toHaveBeenCalled(); - }); - - it('should emit the attachment event after everything is added to the DOM', () => { - const config = new OverlayConfig({ hasBackdrop: true }); - const overlayRef = overlay.create(config); - - overlayRef.attachments().subscribe(() => { - expect(overlayContainerElement.querySelector('pizza')) - .toBeTruthy('Expected the overlay to have been attached.'); - - expect(overlayContainerElement.querySelector('.cdk-overlay-backdrop')) - .toBeTruthy('Expected the backdrop to have been attached.'); - }); - - overlayRef.attach(componentPortal); - }); - - it('should emit when an overlay is detached', () => { - const overlayRef = overlay.create(); - const spy = jasmine.createSpy('detachments spy'); - - overlayRef.detachments().subscribe(spy); - overlayRef.attach(componentPortal); - overlayRef.detach(); - - expect(spy).toHaveBeenCalled(); - }); - - it('should not emit to the detach stream if the overlay has not been attached', () => { - const overlayRef = overlay.create(); - const spy = jasmine.createSpy('detachments spy'); - - overlayRef.detachments().subscribe(spy); - overlayRef.detach(); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('should not emit to the detach stream on dispose if the overlay was not attached', () => { - const overlayRef = overlay.create(); - const spy = jasmine.createSpy('detachments spy'); - - overlayRef.detachments().subscribe(spy); - overlayRef.dispose(); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('should emit the detachment event after the overlay is removed from the DOM', () => { - const overlayRef = overlay.create(); - - overlayRef.detachments().subscribe(() => { - expect(overlayContainerElement.querySelector('pizza')) - .toBeFalsy('Expected the overlay to have been detached.'); - }); - - overlayRef.attach(componentPortal); - overlayRef.detach(); - }); - - it('should emit and complete the observables when an overlay is disposed', () => { - const overlayRef = overlay.create(); - const disposeSpy = jasmine.createSpy('dispose spy'); - const attachCompleteSpy = jasmine.createSpy('attachCompleteSpy spy'); - const detachCompleteSpy = jasmine.createSpy('detachCompleteSpy spy'); - - overlayRef.attachments().subscribe({complete: attachCompleteSpy}); - overlayRef.detachments().subscribe({next: disposeSpy, complete: detachCompleteSpy}); - - overlayRef.attach(componentPortal); - overlayRef.dispose(); - - expect(disposeSpy).toHaveBeenCalled(); - expect(attachCompleteSpy).toHaveBeenCalled(); - expect(detachCompleteSpy).toHaveBeenCalled(); - }); - - it('should complete the attachment observable before the detachment one', () => { - const overlayRef = overlay.create(); - const callbackOrder: string[] = []; - - overlayRef.attachments().subscribe({complete: () => callbackOrder.push('attach')}); - overlayRef.detachments().subscribe({complete: () => callbackOrder.push('detach')}); - - overlayRef.attach(componentPortal); - overlayRef.dispose(); - - expect(callbackOrder).toEqual(['attach', 'detach']); - }); - - it('should default to the ltr direction', () => { - const overlayRef = overlay.create(); - expect(overlayRef.getConfig().direction).toBe('ltr'); - }); - - it('should skip undefined values when applying the defaults', () => { - const overlayRef = overlay.create({ direction: undefined }); - expect(overlayRef.getConfig().direction).toBe('ltr'); - }); - - it('should clear out all DOM element references on dispose', fakeAsync(() => { - const overlayRef = overlay.create({ hasBackdrop: true }); - overlayRef.attach(componentPortal); - - expect(overlayRef.hostElement).toBeTruthy('Expected overlay host to be defined.'); - expect(overlayRef.overlayElement).toBeTruthy('Expected overlay element to be defined.'); - expect(overlayRef.backdropElement).toBeTruthy('Expected backdrop element to be defined.'); - - overlayRef.dispose(); - tick(500); // tslint:disable-line - - expect(overlayRef.hostElement).toBeFalsy('Expected overlay host not to be referenced.'); - expect(overlayRef.overlayElement).toBeFalsy('Expected overlay element not to be referenced.'); - expect(overlayRef.backdropElement).toBeFalsy('Expected backdrop element not to be referenced.'); - })); - - it('should be able to use the `Overlay` provider during app initialization', () => { - /** Dummy provider that depends on `Overlay`. */ - @Injectable() - class CustomErrorHandler extends ErrorHandler { - constructor(private _overlay: Overlay) { - super(); - } - - handleError(error: any) { - const overlayRef = this._overlay.create({ hasBackdrop: !!error }); - overlayRef.dispose(); - } - } - - overlayContainer.ngOnDestroy(); - - TestBed - .resetTestingModule() - .configureTestingModule({ - imports: [OverlayModule], - providers: [ - CustomErrorHandler, - { provide: ErrorHandler, useExisting: CustomErrorHandler } - ] - }); - - expect(() => TestBed.compileComponents()).not.toThrow(); - }); - - // it('should keep the direction in sync with the passed in Directionality', () => { - // const customDirectionality = { value: 'rtl', change: new EventEmitter() }; - // const overlayRef = overlay.create({ direction: customDirectionality as Directionality }); - // - // expect(overlayRef.getDirection()).toBe('rtl'); - // customDirectionality.value = 'ltr'; - // expect(overlayRef.getDirection()).toBe('ltr'); - // }); - - describe('positioning', () => { - let config: OverlayConfig; - - beforeEach(() => { - config = new OverlayConfig(); - }); - - it('should apply the positioning strategy', fakeAsync(() => { - config.positionStrategy = new FakePositionStrategy(); - - overlay.create(config).attach(componentPortal); - viewContainerFixture.detectChanges(); - tick(); - - expect(overlayContainerElement.querySelectorAll('.fake-positioned').length).toBe(1); - })); - - it('should not apply the position if it detaches before the zone stabilizes', fakeAsync(() => { - config.positionStrategy = new FakePositionStrategy(); - - const overlayRef = overlay.create(config); - - spyOn(config.positionStrategy, 'apply'); - - overlayRef.attach(componentPortal); - overlayRef.detach(); - viewContainerFixture.detectChanges(); - tick(); - - // tslint:disable-next-line - expect(config.positionStrategy.apply).not.toHaveBeenCalled(); - })); - - }); - - describe('size', () => { - let config: OverlayConfig; - - beforeEach(() => { - config = new OverlayConfig(); - }); - - it('should apply the width set in the config', () => { - config.width = 500; // tslint:disable-line - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.width).toEqual('500px'); - }); - - it('should support using other units if a string width is provided', () => { - config.width = '200%'; - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.width).toEqual('200%'); - }); - - it('should apply the height set in the config', () => { - config.height = 500; // tslint:disable-line - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.height).toEqual('500px'); - }); - - it('should support using other units if a string height is provided', () => { - config.height = '100vh'; - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.height).toEqual('100vh'); - }); - - it('should apply the min width set in the config', () => { - config.minWidth = 200; // tslint:disable-line - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.minWidth).toEqual('200px'); - }); - - - it('should apply the min height set in the config', () => { - config.minHeight = 500; // tslint:disable-line - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.minHeight).toEqual('500px'); - }); - - it('should apply the max width set in the config', () => { - config.maxWidth = 200; // tslint:disable-line - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.maxWidth).toEqual('200px'); - }); - - - it('should apply the max height set in the config', () => { - config.maxHeight = 500; // tslint:disable-line - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.maxHeight).toEqual('500px'); - }); - - it('should support zero widths and heights', () => { - config.width = 0; - config.height = 0; - - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - expect(overlayRef.overlayElement.style.width).toEqual('0px'); - expect(overlayRef.overlayElement.style.height).toEqual('0px'); - }); - }); - - describe('backdrop', () => { - let config: OverlayConfig; - - beforeEach(() => { - config = new OverlayConfig(); - config.hasBackdrop = true; - }); - - it('should create and destroy an overlay backdrop', () => { - const overlayRef = overlay.create(config); - overlayRef.attach(componentPortal); - - viewContainerFixture.detectChanges(); - const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - expect(backdrop).toBeTruthy(); - expect(backdrop.classList).not.toContain('cdk-overlay-backdrop-showing'); - - const backdropClickHandler = jasmine.createSpy('backdropClickHander'); - overlayRef.backdropClick().subscribe(backdropClickHandler); - - backdrop.click(); - expect(backdropClickHandler).toHaveBeenCalledWith(jasmine.any(MouseEvent)); - }); - - it('should complete the backdrop click stream once the overlay is destroyed', () => { - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - viewContainerFixture.detectChanges(); - - const completeHandler = jasmine.createSpy('backdrop complete handler'); - - overlayRef.backdropClick().subscribe({complete: completeHandler}); - overlayRef.dispose(); - - expect(completeHandler).toHaveBeenCalled(); - }); - - it('should apply the default overlay backdrop class', () => { - const overlayRef = overlay.create(config); - overlayRef.attach(componentPortal); - viewContainerFixture.detectChanges(); - - const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - expect(backdrop.classList).toContain('cdk-overlay-dark-backdrop'); - }); - - it('should apply a custom class to the backdrop', () => { - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - - const overlayRef = overlay.create(config); - overlayRef.attach(componentPortal); - viewContainerFixture.detectChanges(); - - const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - expect(backdrop.classList).toContain('cdk-overlay-transparent-backdrop'); - }); - - it('should apply multiple custom classes to the overlay', () => { - config.backdropClass = ['custom-class-1', 'custom-class-2']; - - const overlayRef = overlay.create(config); - overlayRef.attach(componentPortal); - viewContainerFixture.detectChanges(); - - const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - expect(backdrop.classList).toContain('custom-class-1'); - expect(backdrop.classList).toContain('custom-class-2'); - }); - - it('should disable the pointer events of a backdrop that is being removed', () => { - const overlayRef = overlay.create(config); - overlayRef.attach(componentPortal); - - viewContainerFixture.detectChanges(); - const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; - - expect(backdrop.style.pointerEvents).toBeFalsy(); - - overlayRef.detach(); - - expect(backdrop.style.pointerEvents).toBe('none'); - }); - - it('should insert the backdrop before the overlay host in the DOM order', () => { - const overlayRef = overlay.create(config); - - overlayRef.attach(componentPortal); - viewContainerFixture.detectChanges(); - - const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); - const host = overlayContainerElement.querySelector('.cdk-overlay-pane')!.parentElement!; // tslint:disable-line - const children = Array.prototype.slice.call(overlayContainerElement.children); - - expect(children.indexOf(backdrop)).toBeGreaterThan(-1); - expect(children.indexOf(host)).toBeGreaterThan(-1); - expect(children.indexOf(backdrop)) - .toBeLessThan(children.indexOf(host), 'Expected backdrop to be before the host in the DOM'); - }); - - }); - - describe('panelClass', () => { - it('should apply a custom overlay pane class', () => { - const config = new OverlayConfig({ panelClass: 'custom-panel-class' }); - - overlay.create(config).attach(componentPortal); - viewContainerFixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - expect(pane.classList).toContain('custom-panel-class'); - }); - - it('should be able to apply multiple classes', () => { - const config = new OverlayConfig({ panelClass: ['custom-class-one', 'custom-class-two'] }); - - overlay.create(config).attach(componentPortal); - viewContainerFixture.detectChanges(); - - const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - - expect(pane.classList).toContain('custom-class-one'); - expect(pane.classList).toContain('custom-class-two'); - }); - }); - - describe('scroll strategy', () => { - let fakeScrollStrategy: FakeScrollStrategy; - let config: OverlayConfig; - let overlayRef: OverlayRef; - - beforeEach(() => { - fakeScrollStrategy = new FakeScrollStrategy(); - config = new OverlayConfig({ scrollStrategy: fakeScrollStrategy }); - overlayRef = overlay.create(config); - }); - - it('should attach the overlay ref to the scroll strategy', () => { - expect(fakeScrollStrategy.overlayRef).toBe(overlayRef, - 'Expected scroll strategy to have been attached to the current overlay ref.'); - }); - - it('should enable the scroll strategy when the overlay is attached', () => { - overlayRef.attach(componentPortal); - expect(fakeScrollStrategy.isEnabled).toBe(true, 'Expected scroll strategy to be enabled.'); - }); - - it('should disable the scroll strategy once the overlay is detached', () => { - overlayRef.attach(componentPortal); - expect(fakeScrollStrategy.isEnabled).toBe(true, 'Expected scroll strategy to be enabled.'); - - overlayRef.detach(); - expect(fakeScrollStrategy.isEnabled).toBe(false, 'Expected scroll strategy to be disabled.'); - }); - - it('should disable the scroll strategy when the overlay is destroyed', () => { - overlayRef.dispose(); - expect(fakeScrollStrategy.isEnabled).toBe(false, 'Expected scroll strategy to be disabled.'); - }); - }); -}); - -/** Simple component for testing ComponentPortal. */ -@Component({ - selector: 'pizza', - template: '

Pizza

' -}) -class PizzaMsg { -} - - -/** Test-bed component that contains a TempatePortal and an ElementRef. */ -@Component({ - template: ` - Cake` -}) -class TestComponentWithTemplatePortals { - @ViewChild(CdkPortal, {static: false}) templatePortal: CdkPortal; - - constructor(public viewContainerRef: ViewContainerRef) { - } -} - -// Create a real (non-test) NgModule as a workaround for -// https://github.com/angular/angular/issues/10760 -const TEST_COMPONENTS = [PizzaMsg, TestComponentWithTemplatePortals]; - -@NgModule({ - imports: [OverlayModule, PortalModule], - exports: TEST_COMPONENTS, - declarations: TEST_COMPONENTS, - entryComponents: TEST_COMPONENTS -}) -class OverlayTestModule { -} - -class FakePositionStrategy implements IPositionStrategy { - element: HTMLElement; - - apply(): void { - this.element.classList.add('fake-positioned'); - } - - attach(overlayRef: OverlayRef) { - this.element = overlayRef.overlayElement; - } - - dispose() { - } // tslint:disable-line -} - - -class FakeScrollStrategy implements IScrollStrategy { - isEnabled = false; - overlayRef: OverlayRef; - - attach(overlayRef: OverlayRef) { - this.overlayRef = overlayRef; - } - - enable() { - this.isEnabled = true; - } - - disable() { - this.isEnabled = false; - } -} diff --git a/packages/cdk/overlay/overlay.ts b/packages/cdk/overlay/overlay.ts deleted file mode 100644 index 6a29b49a1..000000000 --- a/packages/cdk/overlay/overlay.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { DOCUMENT } from '@angular/common'; -import { - ApplicationRef, - ComponentFactoryResolver, - Inject, - Injectable, - Injector, - NgZone -} from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { DomPortalOutlet } from '@ptsecurity/cdk/portal'; - -import { OverlayKeyboardDispatcher } from './keyboard/overlay-keyboard-dispatcher'; -import { OverlayConfig } from './overlay-config'; -import { OverlayContainer } from './overlay-container'; -import { OverlayRef } from './overlay-ref'; -import { OverlayPositionBuilder } from './position/overlay-position-builder'; -import { ScrollStrategyOptions } from './scroll/index'; - - -/** Next overlay unique ID. */ -let nextUniqueId = 0; - -// Note that Overlay is *not* scoped to the app root because the ComponentFactoryResolver -// it needs is different based on where OverlayModule is imported. - -/** - * Service to create Overlays. Overlays are dynamically added pieces of floating UI, meant to be - * used as a low-level building block for other components. Dialogs, tooltips, menus, - * selects, etc. can all be built using overlays. The service should primarily be used by authors - * of re-usable components rather than developers building end-user applications. - * - * An overlay *is* a PortalOutlet, so any kind of Portal can be loaded into one. - */ -@Injectable() -export class Overlay { - private _appRef: ApplicationRef; - - constructor( - /** Scrolling strategies that can be used when creating an overlay. */ - public scrollStrategies: ScrollStrategyOptions, - private _overlayContainer: OverlayContainer, - private _componentFactoryResolver: ComponentFactoryResolver, - private _positionBuilder: OverlayPositionBuilder, - private _keyboardDispatcher: OverlayKeyboardDispatcher, - private _injector: Injector, - private _ngZone: NgZone, - @Inject(DOCUMENT) private _document: any, - private _directionality: Directionality - ) {} - - /** - * Creates an overlay. - * @param config Configuration applied to the overlay. - * @returns Reference to the created overlay. - */ - create(config?: OverlayConfig): OverlayRef { - const host = this._createHostElement(); - const pane = this._createPaneElement(host); - const portalOutlet = this._createPortalOutlet(pane); - const overlayConfig = new OverlayConfig(config); - - overlayConfig.direction = overlayConfig.direction || this._directionality.value; - - return new OverlayRef(portalOutlet, host, pane, overlayConfig, this._ngZone, - this._keyboardDispatcher, this._document); - } - - /** - * Gets a position builder that can be used, via fluent API, - * to construct and configure a position strategy. - * @returns An overlay position builder. - */ - position(): OverlayPositionBuilder { - return this._positionBuilder; - } - - /** - * Creates the DOM element for an overlay and appends it to the overlay container. - * @returns Newly-created pane element - */ - private _createPaneElement(host: HTMLElement): HTMLElement { - const pane = this._document.createElement('div'); - - pane.id = `cdk-overlay-${nextUniqueId++}`; - pane.classList.add('cdk-overlay-pane'); - host.appendChild(pane); - - return pane; - } - - /** - * Creates the host element that wraps around an overlay - * and can be used for advanced positioning. - * @returns Newly-create host element. - */ - private _createHostElement(): HTMLElement { - const host = this._document.createElement('div'); - this._overlayContainer.getContainerElement().appendChild(host); - - return host; - } - - /** - * Create a DomPortalOutlet into which the overlay content can be loaded. - * @param pane The DOM element to turn into a portal outlet. - * @returns A portal outlet for the given DOM element. - */ - private _createPortalOutlet(pane: HTMLElement): DomPortalOutlet { - // We have to resolve the ApplicationRef later in order to allow people - // to use overlay-based providers during app initialization. - if (!this._appRef) { - this._appRef = this._injector.get(ApplicationRef); - } - - return new DomPortalOutlet(pane, this._componentFactoryResolver, this._appRef, this._injector); - } -} diff --git a/packages/cdk/overlay/position/connected-position-strategy.spec.ts b/packages/cdk/overlay/position/connected-position-strategy.spec.ts deleted file mode 100644 index a91e61776..000000000 --- a/packages/cdk/overlay/position/connected-position-strategy.spec.ts +++ /dev/null @@ -1,489 +0,0 @@ -import { Component, ElementRef, NgModule, NgZone } from '@angular/core'; -import { inject, TestBed } from '@angular/core/testing'; -import { ComponentPortal, PortalModule } from '@ptsecurity/cdk/portal'; -import { CdkScrollable, ScrollDispatchModule } from '@ptsecurity/cdk/scrolling'; -import { MockNgZone } from '@ptsecurity/cdk/testing'; -import { Subscription } from 'rxjs'; - -import { - ConnectedOverlayPositionChange, - ConnectedPositionStrategy, - ConnectionPositionPair, - Overlay, - OverlayContainer, - OverlayModule, - OverlayRef -} from '../index'; - - -// Default width and height of the overlay and origin panels throughout these tests. -const DEFAULT_HEIGHT = 30; -const DEFAULT_WIDTH = 60; - -// For all tests, we assume the browser window is 1024x786 (outerWidth x outerHeight). -// The karma config has been set to this for local tests, and it is the default size -// for tests on CI (both SauceLabs and Browserstack). -describe('ConnectedPositionStrategy', () => { - let overlay: Overlay; - let overlayContainer: OverlayContainer; - let zone: MockNgZone; - let overlayRef: OverlayRef; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ScrollDispatchModule, OverlayModule, OverlayTestModule], - providers: [{ provide: NgZone, useFactory: () => zone = new MockNgZone() }] - }); - - inject([Overlay, OverlayContainer], (o: Overlay, oc: OverlayContainer) => { - overlay = o; - overlayContainer = oc; - })(); - }); - - afterEach(() => { - overlayContainer.ngOnDestroy(); - - if (overlayRef) { - overlayRef.dispose(); - } - }); - - function attachOverlay(positionStrategy: ConnectedPositionStrategy) { - overlayRef = overlay.create({ positionStrategy }); - overlayRef.attach(new ComponentPortal(TestOverlay)); - zone.simulateZoneExit(); - } - - describe('with origin on document body', () => { - const ORIGIN_HEIGHT = DEFAULT_HEIGHT; - const ORIGIN_WIDTH = DEFAULT_WIDTH; - const OVERLAY_HEIGHT = DEFAULT_HEIGHT; - const OVERLAY_WIDTH = DEFAULT_WIDTH; - - let originElement: HTMLElement; - let positionStrategy: ConnectedPositionStrategy; - let fakeElementRef: ElementRef; - - let originRect: ClientRect | null; - let originCenterX: number | null; - let originCenterY: number | null; - - beforeEach(() => { - // The origin and overlay elements need to be in the document body in order to have geometry. - originElement = createPositionedBlockElement(); - document.body.appendChild(originElement); - fakeElementRef = new ElementRef(originElement); - }); - - afterEach(() => { - document.body.removeChild(originElement); - - // Reset the origin geometry after each test so we don't accidently keep state between tests. - originRect = null; - originCenterX = null; - originCenterY = null; - }); - - describe('when not near viewport edge, not scrolled', () => { - // Place the original element close to the center of the window. - // (1024 / 2, 768 / 2). It's not exact, since outerWidth/Height includes browser - // chrome, but it doesn't really matter for these tests. - const ORIGIN_LEFT = 500; - const ORIGIN_TOP = 350; - - beforeEach(() => { - originElement.style.left = `${ORIGIN_LEFT}px`; - originElement.style.top = `${ORIGIN_TOP}px`; - - originRect = originElement.getBoundingClientRect(); - originCenterX = originRect.left + (ORIGIN_WIDTH / 2); //tslint:disable-line - originCenterY = originRect.top + (ORIGIN_HEIGHT / 2); //tslint:disable-line - }); - }); - - describe('when scrolled', () => { - // Place the original element decently far outside the unscrolled document (1024x768). - const ORIGIN_LEFT = 2500; - const ORIGIN_TOP = 2500; - - // Create a very large element that will make the page scrollable. - let veryLargeElement: HTMLElement = document.createElement('div'); //tslint:disable-line - veryLargeElement.style.width = '4000px'; - veryLargeElement.style.height = '4000px'; - - beforeEach(() => { - // Scroll the page such that the origin element is roughly in the - // center of the visible viewport (2500 - 1024/2, 2500 - 768/2). - document.body.appendChild(veryLargeElement); - document.body.scrollTop = 2100; //tslint:disable-line - document.body.scrollLeft = 2100; //tslint:disable-line - - originElement.style.top = `${ORIGIN_TOP}px`; - originElement.style.left = `${ORIGIN_LEFT}px`; - - originRect = originElement.getBoundingClientRect(); - originCenterX = originRect.left + (ORIGIN_WIDTH / 2); //tslint:disable-line - originCenterY = originRect.top + (ORIGIN_HEIGHT / 2); //tslint:disable-line - }); - - afterEach(() => { - document.body.removeChild(veryLargeElement); - document.body.scrollTop = 0; - document.body.scrollLeft = 0; - }); - - }); - - describe('when near viewport edge', () => { - - it('should position a panel with the x offset provided', () => { - originRect = originElement.getBoundingClientRect(); - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'start', originY: 'top' }, - { overlayX: 'start', overlayY: 'top' }); - - positionStrategy.withOffsetX(10); //tslint:disable-line - attachOverlay(positionStrategy); - - let overlayRect = overlayRef.overlayElement.getBoundingClientRect(); //tslint:disable-line - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top)); - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 10)); //tslint:disable-line - }); - - it('should position a panel with the y offset provided', () => { - originRect = originElement.getBoundingClientRect(); - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'start', originY: 'top' }, - { overlayX: 'start', overlayY: 'top' }); - - positionStrategy.withOffsetY(50); //tslint:disable-line - attachOverlay(positionStrategy); - - let overlayRect = overlayRef.overlayElement.getBoundingClientRect(); //tslint:disable-line - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top + 50)); //tslint:disable-line - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left)); - }); - - }); - - it('should emit onPositionChange event when position changes', () => { - originElement.style.top = '200px'; - originElement.style.right = '25px'; - - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'end', originY: 'center' }, - { overlayX: 'start', overlayY: 'center' }) - .withFallbackPosition( - { originX: 'start', originY: 'bottom' }, - { overlayX: 'end', overlayY: 'top' }); - - const positionChangeHandler = jasmine.createSpy('positionChangeHandler'); - const subscription = positionStrategy.onPositionChange.subscribe(positionChangeHandler); - - attachOverlay(positionStrategy); - - const latestCall = positionChangeHandler.calls.mostRecent(); - - expect(positionChangeHandler).toHaveBeenCalled(); - expect(latestCall.args[0] instanceof ConnectedOverlayPositionChange) - .toBe(true, `Expected strategy to emit an instance of ConnectedOverlayPositionChange.`); - - // If the strategy is re-applied and the initial position would now fit, - // the position change event should be emitted again. - originElement.style.top = '200px'; - originElement.style.left = '200px'; - - positionStrategy.apply(); - - expect(positionChangeHandler).toHaveBeenCalledTimes(2); //tslint:disable-line - - subscription.unsubscribe(); - }); - - it('should emit the onPositionChange event even if none of the positions fit', () => { - originElement.style.bottom = '25px'; - originElement.style.right = '25px'; - - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'end', originY: 'bottom' }, - { overlayX: 'start', overlayY: 'top' }) - .withFallbackPosition( - { originX: 'start', originY: 'bottom' }, - { overlayX: 'end', overlayY: 'top' }); - - const positionChangeHandler = jasmine.createSpy('positionChangeHandler'); - const subscription = positionStrategy.onPositionChange.subscribe(positionChangeHandler); - - attachOverlay(positionStrategy); - - expect(positionChangeHandler).toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - - it('should complete the onPositionChange stream on dispose', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'end', originY: 'bottom' }, - { overlayX: 'start', overlayY: 'top' }); - - const completeHandler = jasmine.createSpy('complete handler'); - - positionStrategy.onPositionChange.subscribe({complete: completeHandler}); - attachOverlay(positionStrategy); - positionStrategy.dispose(); - - expect(completeHandler).toHaveBeenCalled(); - }); - - it('should re-use the preferred position when re-applying while locked in', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'end', originY: 'center' }, - { overlayX: 'start', overlayY: 'center' }) - .withLockedPosition(true) - .withFallbackPosition( - { originX: 'start', originY: 'bottom' }, - { overlayX: 'end', overlayY: 'top' }); - - const recalcSpy = spyOn(positionStrategy._positionStrategy, 'reapplyLastPosition'); - - attachOverlay(positionStrategy); - - expect(recalcSpy).not.toHaveBeenCalled(); - - positionStrategy.apply(); - - expect(recalcSpy).toHaveBeenCalled(); - }); - - }); - - describe('onPositionChange with scrollable view properties', () => { - let scrollable: HTMLDivElement; - let positionChangeHandler: jasmine.Spy; - let onPositionChangeSubscription: Subscription; - let positionChange: ConnectedOverlayPositionChange; - let fakeElementRef: ElementRef; - let positionStrategy: ConnectedPositionStrategy; - - beforeEach(() => { - // Set up the origin - let originElement = createBlockElement(); //tslint:disable-line - originElement.style.margin = '0 1000px 1000px 0'; // Added so that the container scrolls - // Create a scrollable container and put the origin inside - scrollable = createOverflowContainerElement(); - document.body.appendChild(scrollable); - scrollable.appendChild(originElement); - - // Create a strategy with knowledge of the scrollable container - fakeElementRef = new ElementRef(originElement); - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'start', originY: 'bottom' }, - { overlayX: 'start', overlayY: 'top' }); - - positionStrategy.withScrollableContainers([ - new CdkScrollable(new ElementRef(scrollable), null!, null!)]); //tslint:disable-line - positionChangeHandler = jasmine.createSpy('positionChangeHandler'); - onPositionChangeSubscription = - positionStrategy.onPositionChange.subscribe(positionChangeHandler); - attachOverlay(positionStrategy); - }); - - afterEach(() => { - onPositionChangeSubscription.unsubscribe(); - document.body.removeChild(scrollable); - }); - - it('should not have origin or overlay clipped or out of view without scroll', () => { - expect(positionChangeHandler).toHaveBeenCalled(); - }); - - it('should evaluate if origin is clipped if scrolled slightly down', () => { - scrollable.scrollTop = 10; //tslint:disable-line - positionStrategy.apply(); - - expect(positionChangeHandler).toHaveBeenCalled(); - }); - - it('should evaluate if origin is out of view and overlay is clipped if scrolled enough', () => { - scrollable.scrollTop = 31; //tslint:disable-line - positionStrategy.apply(); - - expect(positionChangeHandler).toHaveBeenCalled(); - }); - - it('should evaluate the overlay and origin are both out of the view', () => { - scrollable.scrollTop = 61; //tslint:disable-line - positionStrategy.apply(); - - expect(positionChangeHandler).toHaveBeenCalled(); - positionChange = positionChangeHandler.calls.mostRecent().args[0]; - expect(positionChange.scrollableViewProperties).toEqual({ - isOriginClipped: true, - isOriginOutsideView: true, - isOverlayClipped: true, - isOverlayOutsideView: true - }); - }); - }); - - describe('positioning properties', () => { - let originElement: HTMLElement; - let positionStrategy: ConnectedPositionStrategy; - let fakeElementRef: ElementRef; - - beforeEach(() => { - // The origin and overlay elements need to be in the document body in order to have geometry. - originElement = createPositionedBlockElement(); - document.body.appendChild(originElement); - fakeElementRef = new ElementRef(originElement); - }); - - afterEach(() => { - document.body.removeChild(originElement); - }); - - describe('in ltr', () => { - it('should use `left` when positioning an element at the start', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'start', originY: 'top' }, - { overlayX: 'start', overlayY: 'top' }); - - - attachOverlay(positionStrategy); - - expect(overlayRef.overlayElement.style.left).toBeTruthy(); - expect(overlayRef.overlayElement.style.right).toBeFalsy(); - }); - - it('should use `right` when positioning an element at the end', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'end', originY: 'top' }, - { overlayX: 'end', overlayY: 'top' }); - - attachOverlay(positionStrategy); - - expect(overlayRef.overlayElement.style.right).toBeTruthy(); - expect(overlayRef.overlayElement.style.left).toBeFalsy(); - }); - - }); - - describe('in rtl', () => { - it('should use `right` when positioning an element at the start', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'start', originY: 'top' }, - { overlayX: 'start', overlayY: 'top' } - ) - .withDirection('rtl'); - - attachOverlay(positionStrategy); - - expect(overlayRef.overlayElement.style.right).toBeTruthy(); - expect(overlayRef.overlayElement.style.left).toBeFalsy(); - }); - - it('should use `left` when positioning an element at the end', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'end', originY: 'top' }, - { overlayX: 'end', overlayY: 'top' } - ).withDirection('rtl'); - - attachOverlay(positionStrategy); - - expect(overlayRef.overlayElement.style.left).toBeTruthy(); - expect(overlayRef.overlayElement.style.right).toBeFalsy(); - }); - }); - - describe('vertical', () => { - it('should use `top` when positioning at element along the top', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'start', originY: 'top' }, - { overlayX: 'start', overlayY: 'top' } - ); - - attachOverlay(positionStrategy); - - expect(overlayRef.overlayElement.style.top).toBeTruthy(); - expect(overlayRef.overlayElement.style.bottom).toBeFalsy(); - }); - - it('should use `bottom` when positioning at element along the bottom', () => { - positionStrategy = overlay.position().connectedTo( - fakeElementRef, - { originX: 'start', originY: 'bottom' }, - { overlayX: 'start', overlayY: 'bottom' } - ); - - attachOverlay(positionStrategy); - - expect(overlayRef.overlayElement.style.bottom).toBeTruthy(); - expect(overlayRef.overlayElement.style.top).toBeFalsy(); - }); - }); - - }); - -}); - -/** Creates an absolutely positioned, display: block element with a default size. */ -function createPositionedBlockElement() { - let element = createBlockElement(); //tslint:disable-line - element.style.position = 'absolute'; - - return element; -} - -/** Creates a block element with a default size. */ -function createBlockElement() { - let element = document.createElement('div'); //tslint:disable-line - element.style.width = `${DEFAULT_WIDTH}px`; - element.style.height = `${DEFAULT_HEIGHT}px`; - element.style.backgroundColor = 'rebeccapurple'; - element.style.zIndex = '100'; - - return element; -} - -/** Creates an overflow container with a set height and width with margin. */ -function createOverflowContainerElement() { - let element = document.createElement('div'); //tslint:disable-line - element.style.position = 'relative'; - element.style.overflow = 'auto'; - element.style.height = '300px'; - element.style.width = '300px'; - element.style.margin = '100px'; - - return element; -} - - -@Component({ - template: ` -
` -}) -class TestOverlay { -} - - -@NgModule({ - imports: [OverlayModule, PortalModule], - exports: [TestOverlay], - declarations: [TestOverlay], - entryComponents: [TestOverlay] -}) -class OverlayTestModule { -} diff --git a/packages/cdk/overlay/position/connected-position-strategy.ts b/packages/cdk/overlay/position/connected-position-strategy.ts deleted file mode 100644 index f1e73e00f..000000000 --- a/packages/cdk/overlay/position/connected-position-strategy.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { ElementRef } from '@angular/core'; -import { Direction } from '@ptsecurity/cdk/bidi'; -import { Platform } from '@ptsecurity/cdk/platform'; -import { CdkScrollable, ViewportRuler } from '@ptsecurity/cdk/scrolling'; -import { Observable } from 'rxjs'; - -import { IOverlayReference } from '../overlay-reference'; - -import { - ConnectedOverlayPositionChange, - ConnectionPositionPair, - IOriginConnectionPosition, - IOverlayConnectionPosition, -} from './connected-position'; -import { FlexibleConnectedPositionStrategy } from './flexible-connected-position-strategy'; -import { IPositionStrategy } from './position-strategy'; - - -/** - * A strategy for positioning overlays. Using this strategy, an overlay is given an - * implicit position relative to some origin element. The relative position is defined in terms of - * a point on the origin element that is connected to a point on the overlay element. For example, - * a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner - * of the overlay. - * @deprecated Use `FlexibleConnectedPositionStrategy` instead. - * @deletion-target 7.0.0 - */ -export class ConnectedPositionStrategy implements IPositionStrategy { - /** - * Reference to the underlying position strategy to which all the API calls are proxied. - * @docs-private - */ - _positionStrategy: FlexibleConnectedPositionStrategy; - - /** Ordered list of preferred positions, from most to least desirable. */ - _preferredPositions: ConnectionPositionPair[] = []; - - /** The overlay to which this strategy is attached. */ - private _overlayRef: IOverlayReference; - - private _direction: Direction | null; - - /** Whether the we're dealing with an RTL context */ - get _isRtl() { - return this._overlayRef.getDirection() === 'rtl'; - } - - /** Emits an event when the connection point changes. */ - get onPositionChange(): Observable { - return this._positionStrategy.positionChanges; - } - - constructor( - originPos: IOriginConnectionPosition, - overlayPos: IOverlayConnectionPosition, - connectedTo: ElementRef, - viewportRuler: ViewportRuler, - document: Document, - // @deletion-target 7.0.0 `platform` parameter to be made required. - platform?: Platform) { - - // Since the `ConnectedPositionStrategy` is deprecated and we don't want to maintain - // the extra logic, we create an instance of the positioning strategy that has some - // defaults that make it behave as the old position strategy and to which we'll - // proxy all of the API calls. - this._positionStrategy = - new FlexibleConnectedPositionStrategy(connectedTo, viewportRuler, document, platform) - .withFlexibleDimensions(false) - .withPush(false) - .withViewportMargin(0); - - this.withFallbackPosition(originPos, overlayPos); - } - - /** Ordered list of preferred positions, from most to least desirable. */ - get positions(): ConnectionPositionPair[] { - return this._preferredPositions; - } - - /** Attach this position strategy to an overlay. */ - attach(overlayRef: IOverlayReference): void { - this._overlayRef = overlayRef; - this._positionStrategy.attach(overlayRef); - - if (this._direction) { - overlayRef.setDirection(this._direction); - this._direction = null; - } - } - - /** Disposes all resources used by the position strategy. */ - dispose() { - this._positionStrategy.dispose(); - } - - /** @docs-private */ - detach() { - this._positionStrategy.detach(); - } - - /** - * Updates the position of the overlay element, using whichever preferred position relative - * to the origin fits on-screen. - * @docs-private - */ - apply(): void { - this._positionStrategy.apply(); - } - - /** - * Re-positions the overlay element with the trigger in its last calculated position, - * even if a position higher in the "preferred positions" list would now fit. This - * allows one to re-align the panel without changing the orientation of the panel. - */ - recalculateLastPosition(): void { - this._positionStrategy.reapplyLastPosition(); - } - - /** - * Sets the list of Scrollable containers that host the origin element so that - * on reposition we can evaluate if it or the overlay has been clipped or outside view. Every - * Scrollable must be an ancestor element of the strategy's origin element. - */ - withScrollableContainers(scrollables: CdkScrollable[]) { - this._positionStrategy.withScrollableContainers(scrollables); - } - - /** - * Adds a new preferred fallback position. - * @param originPos - * @param overlayPos - */ - withFallbackPosition( - originPos: IOriginConnectionPosition, - overlayPos: IOverlayConnectionPosition, - offsetX?: number, - offsetY?: number): this { - - const position = new ConnectionPositionPair(originPos, overlayPos, offsetX, offsetY); - this._preferredPositions.push(position); - this._positionStrategy.withPositions(this._preferredPositions); - - return this; - } - - /** - * Sets the layout direction so the overlay's position can be adjusted to match. - * @param dir New layout direction. - */ - withDirection(dir: 'ltr' | 'rtl'): this { - // Since the direction might be declared before the strategy is attached, - // we save the value in a temporary property and we'll transfer it to the - // overlay ref on attachment. - if (this._overlayRef) { - this._overlayRef.setDirection(dir); - } else { - this._direction = dir; - } - - return this; - } - - /** - * Sets an offset for the overlay's connection point on the x-axis - * @param offset New offset in the X axis. - */ - withOffsetX(offset: number): this { - this._positionStrategy.withDefaultOffsetX(offset); - - return this; - } - - /** - * Sets an offset for the overlay's connection point on the y-axis - * @param offset New offset in the Y axis. - */ - withOffsetY(offset: number): this { - this._positionStrategy.withDefaultOffsetY(offset); - - return this; - } - - /** - * Sets whether the overlay's position should be locked in after it is positioned - * initially. When an overlay is locked in, it won't attempt to reposition itself - * when the position is re-applied (e.g. when the user scrolls away). - * @param isLocked Whether the overlay should locked in. - */ - withLockedPosition(isLocked: boolean): this { - this._positionStrategy.withLockedPosition(isLocked); - - return this; - } - - /** - * Overwrites the current set of positions with an array of new ones. - * @param positions Position pairs to be set on the strategy. - */ - withPositions(positions: ConnectionPositionPair[]): this { - this._preferredPositions = positions.slice(); - this._positionStrategy.withPositions(this._preferredPositions); - - return this; - } - - /** - * Sets the origin element, relative to which to position the overlay. - * @param origin Reference to the new origin element. - */ - setOrigin(origin: ElementRef): this { - this._positionStrategy.setOrigin(origin); - - return this; - } -} diff --git a/packages/cdk/overlay/position/connected-position.ts b/packages/cdk/overlay/position/connected-position.ts deleted file mode 100644 index 2e0ea1305..000000000 --- a/packages/cdk/overlay/position/connected-position.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Optional } from '@angular/core'; - - -export type HorizontalConnectionPos = 'start' | 'center' | 'end'; - -/** Vertical dimension of a connection point on the perimeter of the origin or overlay element. */ -export type VerticalConnectionPos = 'top' | 'center' | 'bottom'; - - -/** A connection point on the origin element. */ -export interface IOriginConnectionPosition { - originX: HorizontalConnectionPos; - originY: VerticalConnectionPos; -} - -/** A connection point on the overlay element. */ -export interface IOverlayConnectionPosition { - overlayX: HorizontalConnectionPos; - overlayY: VerticalConnectionPos; -} - -/** The points of the origin element and the overlay element to connect. */ -export class ConnectionPositionPair { - /** X-axis attachment point for connected overlay origin. Can be 'start', 'end', or 'center'. */ - originX: HorizontalConnectionPos; - /** Y-axis attachment point for connected overlay origin. Can be 'top', 'bottom', or 'center'. */ - originY: VerticalConnectionPos; - /** X-axis attachment point for connected overlay. Can be 'start', 'end', or 'center'. */ - overlayX: HorizontalConnectionPos; - /** Y-axis attachment point for connected overlay. Can be 'top', 'bottom', or 'center'. */ - overlayY: VerticalConnectionPos; - - constructor( - origin: IOriginConnectionPosition, - overlay: IOverlayConnectionPosition, - public offsetX?: number, - public offsetY?: number) { - - this.originX = origin.originX; - this.originY = origin.originY; - this.overlayX = overlay.overlayX; - this.overlayY = overlay.overlayY; - } -} - -/** - * Set of properties regarding the position of the origin and overlay relative to the viewport - * with respect to the containing Scrollable elements. - * - * The overlay and origin are clipped if any part of their bounding client rectangle exceeds the - * bounds of any one of the strategy's Scrollable's bounding client rectangle. - * - * The overlay and origin are outside view if there is no overlap between their bounding client - * rectangle and any one of the strategy's Scrollable's bounding client rectangle. - * - * ----------- ----------- - * | outside | | clipped | - * | view | -------------------------- - * | | | | | | - * ---------- | ----------- | - * -------------------------- | | - * | | | Scrollable | - * | | | | - * | | -------------------------- - * | Scrollable | - * | | - * -------------------------- - * - * @docs-private - */ -export class ScrollingVisibility { - isOriginClipped: boolean; - isOriginOutsideView: boolean; - isOverlayClipped: boolean; - isOverlayOutsideView: boolean; -} - -/** The change event emitted by the strategy when a fallback position is used. */ -export class ConnectedOverlayPositionChange { - constructor( - /** The position used as a result of this change. */ - public connectionPair: ConnectionPositionPair, - /** @docs-private */ - @Optional() public scrollableViewProperties: ScrollingVisibility) { - } -} - -/** - * Validates whether a vertical position property matches the expected values. - * @param property Name of the property being validated. - * @param value Value of the property being validated. - * @docs-private - */ -export function validateVerticalPosition(property: string, value: VerticalConnectionPos) { - if (value !== 'top' && value !== 'bottom' && value !== 'center') { - throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` + - `Expected "top", "bottom" or "center".`); - } -} - -/** - * Validates whether a horizontal position property matches the expected values. - * @param property Name of the property being validated. - * @param value Value of the property being validated. - * @docs-private - */ -export function validateHorizontalPosition(property: string, value: HorizontalConnectionPos) { - if (value !== 'start' && value !== 'end' && value !== 'center') { - throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` + - `Expected "start", "end" or "center".`); - } -} diff --git a/packages/cdk/overlay/position/flexible-connected-position-strategy.spec.ts b/packages/cdk/overlay/position/flexible-connected-position-strategy.spec.ts deleted file mode 100644 index 08e07a829..000000000 --- a/packages/cdk/overlay/position/flexible-connected-position-strategy.spec.ts +++ /dev/null @@ -1,988 +0,0 @@ -import { Component, ElementRef, NgModule, NgZone } from '@angular/core'; -import { inject, TestBed } from '@angular/core/testing'; -import { ComponentPortal, PortalModule } from '@ptsecurity/cdk/portal'; -import { CdkScrollable, ScrollDispatchModule } from '@ptsecurity/cdk/scrolling'; -import { MockNgZone } from '@ptsecurity/cdk/testing'; -import { Subscription } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import { - ConnectedOverlayPositionChange, - FlexibleConnectedPositionStrategy, - Overlay, - OverlayConfig, - OverlayContainer, - OverlayModule, - OverlayRef, - ViewportRuler -} from '../index'; - - -// Default width and height of the overlay and origin panels throughout these tests. -const DEFAULT_HEIGHT = 30; -const DEFAULT_WIDTH = 60; - -describe('FlexibleConnectedPositionStrategy', () => { - let overlay: Overlay; - let overlayContainer: OverlayContainer; - let zone: MockNgZone; - let overlayRef: OverlayRef; - let viewport: ViewportRuler; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ScrollDispatchModule, OverlayModule, OverlayTestModule], - providers: [{ provide: NgZone, useFactory: () => zone = new MockNgZone() }] - }); - - inject([Overlay, OverlayContainer, ViewportRuler], - (o: Overlay, oc: OverlayContainer, v: ViewportRuler) => { - overlay = o; - overlayContainer = oc; - viewport = v; - })(); - }); - - afterEach(() => { - overlayContainer.ngOnDestroy(); - - if (overlayRef) { - overlayRef.dispose(); - } - }); - - function attachOverlay(config: OverlayConfig) { - overlayRef = overlay.create(config); - overlayRef.attach(new ComponentPortal(TestOverlay)); - zone.simulateZoneExit(); - } - - it('should throw when attempting to attach to multiple different overlays', () => { - const origin = document.createElement('div'); - const positionStrategy = overlay.position() - .flexibleConnectedTo(origin) - .withPositions([{ - overlayX: 'start', - overlayY: 'top', - originX: 'start', - originY: 'bottom' - }]); - - // Needs to be in the DOM for IE not to throw an "Unspecified error". - document.body.appendChild(origin); - attachOverlay({ positionStrategy }); - - expect(() => attachOverlay({ positionStrategy })).toThrow(); - - document.body.removeChild(origin); - }); - - it('should not throw when trying to apply after being disposed', () => { - const origin = document.createElement('div'); - const positionStrategy = overlay.position() - .flexibleConnectedTo(origin) - .withPositions([{ - overlayX: 'start', - overlayY: 'top', - originX: 'start', - originY: 'bottom' - }]); - - // Needs to be in the DOM for IE not to throw an "Unspecified error". - document.body.appendChild(origin); - attachOverlay({ positionStrategy }); - overlayRef.dispose(); - - expect(() => positionStrategy.apply()).not.toThrow(); - - document.body.removeChild(origin); - }); - - it('should not throw when trying to re-apply the last position after being disposed', () => { - const origin = document.createElement('div'); - const positionStrategy = overlay.position() - .flexibleConnectedTo(origin) - .withPositions([{ - overlayX: 'start', - overlayY: 'top', - originX: 'start', - originY: 'bottom' - }]); - - // Needs to be in the DOM for IE not to throw an "Unspecified error". - document.body.appendChild(origin); - attachOverlay({ positionStrategy }); - overlayRef.dispose(); - - expect(() => positionStrategy.reapplyLastPosition()).not.toThrow(); - - document.body.removeChild(origin); - }); - - describe('without flexible dimensions and pushing', () => { - const ORIGIN_HEIGHT = DEFAULT_HEIGHT; - const ORIGIN_WIDTH = DEFAULT_WIDTH; - const OVERLAY_HEIGHT = DEFAULT_HEIGHT; - const OVERLAY_WIDTH = DEFAULT_WIDTH; - - let originElement: HTMLElement; - let positionStrategy: FlexibleConnectedPositionStrategy; - - beforeEach(() => { - // The origin and overlay elements need to be in the document body in order to have geometry. - originElement = createPositionedBlockElement(); - document.body.appendChild(originElement); - positionStrategy = overlay.position() - .flexibleConnectedTo(originElement) - .withFlexibleDimensions(false) - .withPush(false); - }); - - afterEach(() => { - document.body.removeChild(originElement); - }); - - describe('when scrolled', () => { - // Place the original element decently far outside the unscrolled document (1024x768). - const ORIGIN_LEFT = 2500; - const ORIGIN_TOP = 2500; - - // Create a very large element that will make the page scrollable. - let veryLargeElement: HTMLElement = document.createElement('div'); //tslint:disable-line - veryLargeElement.style.width = '4000px'; - veryLargeElement.style.height = '4000px'; - - beforeEach(() => { - // Scroll the page such that the origin element is roughly in the - // center of the visible viewport (2500 - 1024/2, 2500 - 768/2). - document.body.appendChild(veryLargeElement); - window.scroll(2100, 2100); //tslint:disable-line - - originElement.style.top = `${ORIGIN_TOP}px`; - originElement.style.left = `${ORIGIN_LEFT}px`; - }); - - afterEach(() => { - window.scroll(0, 0); - document.body.removeChild(veryLargeElement); - }); - }); - - describe('when near viewport edge', () => { - - it('should position a panel with the x offset provided', () => { - const originRect = originElement.getBoundingClientRect(); - - positionStrategy.withPositions([{ - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top', - offsetX: 10 - }]); - - attachOverlay({ positionStrategy }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top)); - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 10)); //tslint:disable-line - }); - - it('should be able to set the default x offset', () => { - const originRect = originElement.getBoundingClientRect(); - - positionStrategy.withDefaultOffsetX(20).withPositions([{ //tslint:disable-line - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top)); - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 20)); //tslint:disable-line - }); - - it('should have the position offset x take precedence over the default offset x', () => { - const originRect = originElement.getBoundingClientRect(); - - positionStrategy.withDefaultOffsetX(20).withPositions([{ //tslint:disable-line - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top', - offsetX: 10 - }]); - - attachOverlay({ positionStrategy }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top)); - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 10)); //tslint:disable-line - }); - - it('should position a panel with the y offset provided', () => { - const originRect = originElement.getBoundingClientRect(); - - positionStrategy.withPositions([{ - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top', - offsetY: 50 - }]); - - attachOverlay({ positionStrategy }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top + 50)); //tslint:disable-line - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left)); - }); - - it('should be able to set the default y offset', () => { - const originRect = originElement.getBoundingClientRect(); - - positionStrategy.withDefaultOffsetY(60).withPositions([{ //tslint:disable-line - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top + 60)); //tslint:disable-line - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left)); - }); - - it('should have the position offset y take precedence over the default offset y', () => { - const originRect = originElement.getBoundingClientRect(); - - positionStrategy.withDefaultOffsetY(60).withPositions([{ //tslint:disable-line - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top', - offsetY: 50 - }]); - - attachOverlay({ positionStrategy }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top + 50)); //tslint:disable-line - expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left)); - }); - - }); - - describe('with transform origin', () => { - it('should set the proper transform-origin when aligning to start/bottom', () => { - positionStrategy.withTransformOriginOn('.transform-origin').withPositions([{ - originX: 'start', - originY: 'bottom', - overlayX: 'start', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy }); - - const target = overlayRef.overlayElement.querySelector('.transform-origin')! as HTMLElement; //tslint:disable-line - - expect(target.style.transformOrigin).toContain('left top'); - }); - - it('should set the proper transform-origin when aligning to end/bottom', () => { - positionStrategy.withTransformOriginOn('.transform-origin').withPositions([{ - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy }); - - const target = overlayRef.overlayElement.querySelector('.transform-origin')! as HTMLElement; //tslint:disable-line - - expect(target.style.transformOrigin).toContain('right top'); - }); - - it('should set the proper transform-origin when centering vertically', () => { - positionStrategy.withTransformOriginOn('.transform-origin').withPositions([{ - originX: 'start', - originY: 'center', - overlayX: 'start', - overlayY: 'center' - }]); - - attachOverlay({ positionStrategy }); - - const target = overlayRef.overlayElement.querySelector('.transform-origin')! as HTMLElement; //tslint:disable-line - - expect(target.style.transformOrigin).toContain('left center'); - }); - - it('should set the proper transform-origin when centering horizontally', () => { - positionStrategy.withTransformOriginOn('.transform-origin').withPositions([{ - originX: 'center', - originY: 'top', - overlayX: 'center', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy }); - - const target = overlayRef.overlayElement.querySelector('.transform-origin')! as HTMLElement; //tslint:disable-line - - expect(target.style.transformOrigin).toContain('center top'); - }); - - it('should set the proper transform-origin when aligning to start/top', () => { - positionStrategy.withTransformOriginOn('.transform-origin').withPositions([{ - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'bottom' - }]); - - attachOverlay({ positionStrategy }); - - const target = overlayRef.overlayElement.querySelector('.transform-origin')! as HTMLElement; //tslint:disable-line - - expect(target.style.transformOrigin).toContain('left bottom'); - }); - - it('should set the proper transform-origin when aligning to start/bottom in rtl', () => { - positionStrategy.withTransformOriginOn('.transform-origin').withPositions([{ - originX: 'start', - originY: 'bottom', - overlayX: 'start', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy, direction: 'rtl' }); - - const target = overlayRef.overlayElement.querySelector('.transform-origin')! as HTMLElement; //tslint:disable-line - - expect(target.style.transformOrigin).toContain('right top'); - }); - - it('should set the proper transform-origin when aligning to end/bottom in rtl', () => { - positionStrategy.withTransformOriginOn('.transform-origin').withPositions([{ - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy, direction: 'rtl' }); - - const target = overlayRef.overlayElement.querySelector('.transform-origin')! as HTMLElement; //tslint:disable-line - - expect(target.style.transformOrigin).toContain('left top'); - }); - - }); - - it('should emit onPositionChange event when the position changes', () => { - originElement.style.top = '200px'; - originElement.style.right = '25px'; - - positionStrategy.withPositions([ - { - originX: 'end', - originY: 'center', - overlayX: 'start', - overlayY: 'center' - }, - { - originX: 'start', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - } - ]); - - const positionChangeHandler = jasmine.createSpy('positionChangeHandler'); - const subscription = positionStrategy.positionChanges.subscribe(positionChangeHandler); - - attachOverlay({ positionStrategy }); - - const latestCall = positionChangeHandler.calls.mostRecent(); - - expect(positionChangeHandler).toHaveBeenCalled(); - expect(latestCall.args[0] instanceof ConnectedOverlayPositionChange) - .toBe(true, `Expected strategy to emit an instance of ConnectedOverlayPositionChange.`); - - // If the strategy is re-applied and the initial position would now fit, - // the position change event should be emitted again. - originElement.style.top = '200px'; - originElement.style.left = '200px'; - - overlayRef.updatePosition(); - - expect(positionChangeHandler).toHaveBeenCalledTimes(2); //tslint:disable-line - - subscription.unsubscribe(); - }); - - it('should emit the onPositionChange event even if none of the positions fit', () => { - originElement.style.bottom = '25px'; - originElement.style.right = '25px'; - - positionStrategy.withPositions([ - { - originX: 'end', - originY: 'bottom', - overlayX: 'start', - overlayY: 'top' - }, - { - originX: 'start', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - } - ]); - - const positionChangeHandler = jasmine.createSpy('positionChangeHandler'); - const subscription = positionStrategy.positionChanges.subscribe(positionChangeHandler); - - attachOverlay({ positionStrategy }); - - expect(positionChangeHandler).toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - - it('should re-use the preferred position when re-applying while locked in', () => { - positionStrategy.withPositions([ - { - originX: 'end', - originY: 'center', - overlayX: 'start', - overlayY: 'center' - }, - { - originX: 'start', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - } - ]) - .withLockedPosition(); - - const recalcSpy = spyOn(positionStrategy, 'reapplyLastPosition'); - - attachOverlay({ positionStrategy }); - - expect(recalcSpy).not.toHaveBeenCalled(); - - positionStrategy.apply(); - - expect(recalcSpy).toHaveBeenCalled(); - }); - }); - - describe('with pushing', () => { - const OVERLAY_HEIGHT = DEFAULT_HEIGHT; - const OVERLAY_WIDTH = DEFAULT_WIDTH; - - let originElement: HTMLElement; - let positionStrategy: FlexibleConnectedPositionStrategy; - - beforeEach(() => { - originElement = createPositionedBlockElement(); - document.body.appendChild(originElement); - positionStrategy = overlay.position() - .flexibleConnectedTo(originElement) - .withFlexibleDimensions(false) - .withPush(); - }); - - afterEach(() => { - document.body.removeChild(originElement); - }); - - }); - - describe('with flexible dimensions', () => { - const OVERLAY_HEIGHT = DEFAULT_HEIGHT; - const OVERLAY_WIDTH = DEFAULT_WIDTH; - - let originElement: HTMLElement; - let positionStrategy: FlexibleConnectedPositionStrategy; - - beforeEach(() => { - originElement = createPositionedBlockElement(); - document.body.appendChild(originElement); - positionStrategy = overlay.position().flexibleConnectedTo(originElement); - }); - - afterEach(() => { - document.body.removeChild(originElement); - }); - - it('should align the overlay to `flex-start` when the content is flowing to the right', () => { - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'top', - overlayX: 'start', - originY: 'bottom', - originX: 'start' - }]); - - attachOverlay({ positionStrategy }); - - expect(overlayRef.hostElement.style.alignItems).toBe('flex-start'); - }); - - it('should align the overlay to `flex-end` when the content is flowing to the left', () => { - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'top', - overlayX: 'end', - originY: 'bottom', - originX: 'end' - }]); - - attachOverlay({ positionStrategy }); - - expect(overlayRef.hostElement.style.alignItems).toBe('flex-end'); - }); - - it('should align the overlay to `center` when the content is centered', () => { - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'top', - overlayX: 'center', - originY: 'bottom', - originX: 'center' - }]); - - attachOverlay({ positionStrategy }); - - expect(overlayRef.hostElement.style.alignItems).toBe('center'); - }); - - it('should become scrollable when it hits the viewport edge with a flexible width', () => { - originElement.style.top = '200px'; - originElement.style.right = '-20px'; - - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'top', - overlayX: 'start', - originY: 'bottom', - originX: 'start' - }]); - - attachOverlay({ positionStrategy }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.width)).toBe(OVERLAY_WIDTH - 20); //tslint:disable-line - }); - - it('should not collapse the height if the size is less than the minHeight', () => { - originElement.style.left = '200px'; - originElement.style.bottom = `${OVERLAY_HEIGHT - 10}px`; //tslint:disable-line - - positionStrategy - .withFlexibleDimensions() - .withPositions([{ - overlayY: 'top', - overlayX: 'start', - originY: 'bottom', - originX: 'start' - }]); - - attachOverlay({ - positionStrategy, - minHeight: OVERLAY_HEIGHT - 5 //tslint:disable-line - }); - - const overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - expect(Math.floor(overlayRect.height)).toBe(OVERLAY_HEIGHT); - }); - - it('should be able to opt-in to having the overlay grow after it was opened', () => { - originElement.style.left = '200px'; - originElement.style.bottom = `${OVERLAY_HEIGHT - 10}px`; //tslint:disable-line - - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withGrowAfterOpen() - .withPositions([{ - overlayY: 'top', - overlayX: 'start', - originY: 'bottom', - originX: 'start' - }]); - - attachOverlay({ positionStrategy }); - - let overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - - originElement.style.bottom = '200px'; - overlayRef.updatePosition(); - overlayRect = overlayRef.overlayElement.getBoundingClientRect(); - - // The overlay should be back to full height. - expect(Math.floor(overlayRect.height)).toBe(OVERLAY_HEIGHT); - }); - - it('should set the proper styles when the `bottom` value is exactly zero', () => { - originElement.style.position = 'fixed'; - originElement.style.bottom = '0'; - originElement.style.left = '200px'; - - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'bottom', - overlayX: 'start', - originY: 'bottom', - originX: 'start' - }]); - - attachOverlay({ positionStrategy }); - - const boundingBox = overlayContainer - .getContainerElement() - .querySelector('.cdk-overlay-connected-position-bounding-box') as HTMLElement; - - // Ensure that `0px` is set explicitly, rather than the - // property being left blank due to zero being falsy. - expect(boundingBox.style.bottom).toBe('0px'); - }); - - it('should set the proper styles when the `top` value is exactly zero', () => { - originElement.style.position = 'fixed'; - originElement.style.top = '0'; - originElement.style.left = '200px'; - - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'top', - overlayX: 'start', - originY: 'top', - originX: 'start' - }]); - - attachOverlay({ positionStrategy }); - - const boundingBox = overlayContainer - .getContainerElement() - .querySelector('.cdk-overlay-connected-position-bounding-box') as HTMLElement; - - // Ensure that `0px` is set explicitly, rather than the - // property being left blank due to zero being falsy. - expect(boundingBox.style.top).toBe('0px'); - }); - - it('should set the proper styles when the `left` value is exactly zero', () => { - originElement.style.position = 'fixed'; - originElement.style.left = '0'; - originElement.style.top = '200px'; - - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'top', - overlayX: 'start', - originY: 'top', - originX: 'start' - }]); - - attachOverlay({ positionStrategy }); - - const boundingBox = overlayContainer - .getContainerElement() - .querySelector('.cdk-overlay-connected-position-bounding-box') as HTMLElement; - - // Ensure that `0px` is set explicitly, rather than the - // property being left blank due to zero being falsy. - expect(boundingBox.style.left).toBe('0px'); - }); - - it('should set the proper styles when the `right` value is exactly zero', () => { - originElement.style.position = 'fixed'; - originElement.style.right = '0'; - originElement.style.top = '200px'; - - positionStrategy - .withFlexibleDimensions() - .withPush(false) - .withPositions([{ - overlayY: 'top', - overlayX: 'end', - originY: 'top', - originX: 'end' - }]); - - attachOverlay({ positionStrategy }); - - const boundingBox = overlayContainer - .getContainerElement() - .querySelector('.cdk-overlay-connected-position-bounding-box') as HTMLElement; - - // Ensure that `0px` is set explicitly, rather than the - // property being left blank due to zero being falsy. - expect(boundingBox.style.right).toBe('0px'); - }); - - }); - - describe('onPositionChange with scrollable view properties', () => { - let scrollable: HTMLDivElement; - let positionChangeHandler: jasmine.Spy; - let onPositionChangeSubscription: Subscription; - - beforeEach(() => { - // Set up the origin - const originElement = createBlockElement(); - originElement.style.margin = '0 1000px 1000px 0'; // Added so that the container scrolls - // Create a scrollable container and put the origin inside - scrollable = createOverflowContainerElement(); - document.body.appendChild(scrollable); - scrollable.appendChild(originElement); - - // Create a strategy with knowledge of the scrollable container - const strategy = overlay.position() - .flexibleConnectedTo(originElement) - .withPush(false) - .withPositions([{ - originX: 'start', - originY: 'bottom', - overlayX: 'start', - overlayY: 'top' - }]); - - strategy.withScrollableContainers([ - new CdkScrollable(new ElementRef(scrollable), null!, null!) //tslint:disable-line - ]); - - positionChangeHandler = jasmine.createSpy('positionChange handler'); - onPositionChangeSubscription = strategy.positionChanges - .pipe(map((event) => event.scrollableViewProperties)) - .subscribe(positionChangeHandler); - - attachOverlay({ positionStrategy: strategy }); - }); - - afterEach(() => { - onPositionChangeSubscription.unsubscribe(); - document.body.removeChild(scrollable); - }); - - it('should evaluate the overlay and origin are both out of the view', () => { - scrollable.scrollTop = 61; //tslint:disable-line - overlayRef.updatePosition(); - - expect(positionChangeHandler).toHaveBeenCalledWith(jasmine.objectContaining({ - isOriginClipped: true, - isOriginOutsideView: true, - isOverlayClipped: true, - isOverlayOutsideView: true - })); - }); - }); - - describe('positioning properties', () => { - let originElement: HTMLElement; - let positionStrategy: FlexibleConnectedPositionStrategy; - - beforeEach(() => { - originElement = createPositionedBlockElement(); - document.body.appendChild(originElement); - positionStrategy = overlay.position().flexibleConnectedTo(originElement); - }); - - afterEach(() => { - document.body.removeChild(originElement); - }); - - describe('in ltr', () => { - it('should use `left` when positioning an element at the start', () => { - positionStrategy.withPositions([{ - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy }); - - expect(overlayRef.hostElement.style.left).toBeTruthy(); - expect(overlayRef.hostElement.style.right).toBeFalsy(); - }); - }); - - describe('in rtl', () => { - it('should use `left` when positioning an element at the end', () => { - positionStrategy.withPositions([{ - originX: 'end', - originY: 'top', - overlayX: 'end', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy, direction: 'rtl' }); - - expect(overlayRef.hostElement.style.left).toBeTruthy(); - expect(overlayRef.hostElement.style.right).toBeFalsy(); - }); - }); - - describe('vertical', () => { - it('should use `top` when positioning at element along the top', () => { - positionStrategy.withPositions([{ - originX: 'start', - originY: 'top', - overlayX: 'start', - overlayY: 'top' - }]); - - attachOverlay({ positionStrategy }); - - expect(overlayRef.hostElement.style.top).toBeTruthy(); - expect(overlayRef.hostElement.style.bottom).toBeFalsy(); - }); - - it('should use `bottom` when positioning at element along the bottom', () => { - positionStrategy.withPositions([{ - originX: 'start', - originY: 'bottom', - overlayX: 'start', - overlayY: 'bottom' - }]); - - attachOverlay({ positionStrategy }); - - expect(overlayRef.hostElement.style.bottom).toBeTruthy(); - expect(overlayRef.hostElement.style.top).toBeFalsy(); - }); - }); - - }); - - describe('validations', () => { - let originElement: HTMLElement; - let positionStrategy: FlexibleConnectedPositionStrategy; - - beforeEach(() => { - originElement = createPositionedBlockElement(); - document.body.appendChild(originElement); - positionStrategy = overlay.position().flexibleConnectedTo(originElement); - }); - - afterEach(() => { - document.body.removeChild(originElement); - positionStrategy.dispose(); - }); - - it('should throw when attaching without any positions', () => { - expect(() => positionStrategy.withPositions([])).toThrow(); - }); - - it('should throw when passing in something that is missing a connection point', () => { - expect(() => { - positionStrategy.withPositions([{ - originY: 'top', - overlayX: 'start', - overlayY: 'top' - } as any]); - }).toThrow(); - }); - - it('should throw when passing in something that has an invalid X position', () => { - expect(() => { - positionStrategy.withPositions([{ - originX: 'left', - originY: 'top', - overlayX: 'left', - overlayY: 'top' - } as any]); - }).toThrow(); - }); - - it('should throw when passing in something that has an invalid Y position', () => { - expect(() => { - positionStrategy.withPositions([{ - originX: 'start', - originY: 'middle', - overlayX: 'start', - overlayY: 'middle' - } as any]); - }).toThrow(); - }); - }); - -}); - -/** Creates an absolutely positioned, display: block element with a default size. */ -function createPositionedBlockElement() { - const element = createBlockElement(); - element.style.position = 'absolute'; - - return element; -} - -/** Creates a block element with a default size. */ -function createBlockElement() { - const element = document.createElement('div'); - element.style.width = `${DEFAULT_WIDTH}px`; - element.style.height = `${DEFAULT_HEIGHT}px`; - element.style.backgroundColor = 'rebeccapurple'; - element.style.zIndex = '100'; - - return element; -} - -/** Creates an overflow container with a set height and width with margin. */ -function createOverflowContainerElement() { - const element = document.createElement('div'); - element.style.position = 'relative'; - element.style.overflow = 'auto'; - element.style.height = '300px'; - element.style.width = '300px'; - element.style.margin = '100px'; - - return element; -} - - -@Component({ - template: ` -
- ` -}) -class TestOverlay { -} - - -@NgModule({ - imports: [OverlayModule, PortalModule], - exports: [TestOverlay], - declarations: [TestOverlay], - entryComponents: [TestOverlay] -}) -class OverlayTestModule { -} diff --git a/packages/cdk/overlay/position/flexible-connected-position-strategy.ts b/packages/cdk/overlay/position/flexible-connected-position-strategy.ts deleted file mode 100644 index 88948da84..000000000 --- a/packages/cdk/overlay/position/flexible-connected-position-strategy.ts +++ /dev/null @@ -1,1034 +0,0 @@ -import { ElementRef } from '@angular/core'; -import { coerceCssPixelValue } from '@ptsecurity/cdk/coercion'; -import { Platform } from '@ptsecurity/cdk/platform'; -import { ViewportRuler, CdkScrollable } from '@ptsecurity/cdk/scrolling'; -import { Observable, Subscription, Subject } from 'rxjs'; - -import { IOverlayReference } from '../overlay-reference'; - -import { - ConnectedOverlayPositionChange, - ConnectionPositionPair, - ScrollingVisibility, - validateHorizontalPosition, - validateVerticalPosition -} from './connected-position'; -import { IPositionStrategy } from './position-strategy'; -import { isElementScrolledOutsideView, isElementClippedByScrolling } from './scroll-clip'; - - -/** - * A strategy for positioning overlays. Using this strategy, an overlay is given an - * implicit position relative some origin element. The relative position is defined in terms of - * a point on the origin element that is connected to a point on the overlay element. For example, - * a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner - * of the overlay. - */ -export class FlexibleConnectedPositionStrategy implements IPositionStrategy { - - /** Ordered list of preferred positions, from most to least desirable. */ - _preferredPositions: ConnectionPositionPair[] = []; - - /** Ordered list of preferred positions, from most to least desirable. */ - get positions(): ConnectionPositionPair[] { - return this._preferredPositions; - } - - /** The overlay to which this strategy is attached. */ - private _overlayRef: IOverlayReference; - - /** Whether we're performing the very first positioning of the overlay. */ - private _isInitialRender = true; - - /** Last size used for the bounding box. Used to avoid resizing the overlay after open. */ - private _lastBoundingBoxSize = { width: 0, height: 0 }; - - /** Whether the overlay was pushed in a previous positioning. */ - private _isPushed = false; - - /** Whether the overlay can be pushed on-screen on the initial open. */ - private _canPush = true; - - /** Whether the overlay can grow via flexible width/height after the initial open. */ - private _growAfterOpen = false; - - /** Whether the overlay's width and height can be constrained to fit within the viewport. */ - private _hasFlexibleDimensions = true; - - /** Whether the overlay position is locked. */ - private _positionLocked = false; - - /** Cached origin dimensions */ - private _originRect: ClientRect; - - /** Cached overlay dimensions */ - private _overlayRect: ClientRect; - - /** Cached viewport dimensions */ - private _viewportRect: ClientRect; - - /** Amount of space that must be maintained between the overlay and the edge of the viewport. */ - private _viewportMargin = 0; - - /** The Scrollable containers used to check scrollable view properties on position change. */ - private scrollables: CdkScrollable[] = []; - - /** The origin element against which the overlay will be positioned. */ - private _origin: HTMLElement; - - /** The overlay pane element. */ - private _pane: HTMLElement; - - /** Whether the strategy has been disposed of already. */ - private _isDisposed: boolean; - - /** - * Parent element for the overlay panel used to constrain the overlay panel's size to fit - * within the viewport. - */ - private _boundingBox: HTMLElement | null; - - /** The last position to have been calculated as the best fit position. */ - private _lastPosition: IConnectedPosition | null; - - /** Subject that emits whenever the position changes. */ - private _positionChanges = new Subject(); - - /** Subscription to viewport size changes. */ - private _resizeSubscription = Subscription.EMPTY; - - /** Default offset for the overlay along the x axis. */ - private _offsetX = 0; - - /** Default offset for the overlay along the y axis. */ - private _offsetY = 0; - - /** Selector to be used when finding the elements on which to set the transform origin. */ - private _transformOriginSelector: string; - - /** Observable sequence of position changes. */ - positionChanges: Observable = - this._positionChanges.asObservable(); - - constructor( - connectedTo: ElementRef | HTMLElement, - private _viewportRuler: ViewportRuler, - private _document: Document, - // @deletion-target 7.0.0 `_platform` parameter to be made required. - private _platform?: Platform) { - this.setOrigin(connectedTo); - } - - /** Attaches this position strategy to an overlay. */ - attach(overlayRef: IOverlayReference): void { - if (this._overlayRef && overlayRef !== this._overlayRef) { - throw Error('This position strategy is already attached to an overlay'); - } - - this._validatePositions(); - - overlayRef.hostElement.classList.add('cdk-overlay-connected-position-bounding-box'); - - this._overlayRef = overlayRef; - this._boundingBox = overlayRef.hostElement; - this._pane = overlayRef.overlayElement; - this._resizeSubscription.unsubscribe(); - this._resizeSubscription = this._viewportRuler.change().subscribe(() => this.apply()); - } - - /** - * Updates the position of the overlay element, using whichever preferred position relative - * to the origin best fits on-screen. - * - * The selection of a position goes as follows: - * - If any positions fit completely within the viewport as-is, - * choose the first position that does so. - * - If flexible dimensions are enabled and at least one satifies the given minimum width/height, - * choose the position with the greatest available size modified by the positions' weight. - * - If pushing is enabled, take the position that went off-screen the least and push it - * on-screen. - * - If none of the previous criteria were met, use the position that goes off-screen the least. - * @docs-private - */ - apply(): void { - // We shouldn't do anything if the strategy was disposed or we're on the server. - if (this._isDisposed || (this._platform && !this._platform.isBrowser)) { - return; - } - // If the position has been applied already (e.g. when the overlay was opened) and the - // consumer opted into locking in the position, re-use the old position, in order to - // prevent the overlay from jumping around. - if (!this._isInitialRender && this._positionLocked && this._lastPosition) { - this.reapplyLastPosition(); - - return; - } - this._resetOverlayElementStyles(); - this._resetBoundingBoxStyles(); - // We need the bounding rects for the origin and the overlay to determine how to position - // the overlay relative to the origin. - // We use the viewport rect to determine whether a position would go off-screen. - this._viewportRect = this._getNarrowedViewportRect(); - this._originRect = this._origin.getBoundingClientRect(); - this._overlayRect = this._pane.getBoundingClientRect(); - - const originRect = this._originRect; - const overlayRect = this._overlayRect; - const viewportRect = this._viewportRect; - // Positions where the overlay will fit with flexible dimensions. - const flexibleFits: IFlexibleFit[] = []; - // Fallback if none of the preferred positions fit within the viewport. - let fallback: IFallbackPosition | undefined; - - // Go through each of the preferred positions looking for a good fit. - // If a good fit is found, it will be applied immediately. - for (let pos of this._preferredPositions) { //tslint:disable-line - // Get the exact (x, y) coordinate for the point-of-origin on the origin element. - let originPoint = this._getOriginPoint(originRect, pos); //tslint:disable-line - // From that point-of-origin, get the exact (x, y) coordinate for the top-left corner of the - // overlay in this position. We use the top-left corner for calculations and later translate - // this into an appropriate (top, left, bottom, right) style. - let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos); //tslint:disable-line - // Calculate how well the overlay would fit into the viewport with this point. - let overlayFit = this._getOverlayFit(overlayPoint, overlayRect, viewportRect, pos); //tslint:disable-line - // If the overlay, without any further work, fits into the viewport, use this position. - if (overlayFit.isCompletelyWithinViewport) { - this._isPushed = false; - this._applyPosition(pos, originPoint); - - return; - } - // If the overlay has flexible dimensions, we can use this position - // so long as there's enough space for the minimum dimensions. - if (this._canFitWithFlexibleDimensions(overlayFit, overlayPoint, viewportRect)) { - // Save positions where the overlay will fit with flexible dimensions. We will use these - // if none of the positions fit *without* flexible dimensions. - flexibleFits.push({ - position: pos, - origin: originPoint, - overlayRect, - boundingBoxRect: this._calculateBoundingBoxRect(originPoint, pos) - }); - - continue; - } - - // If the current preferred position does not fit on the screen, remember the position - // if it has more visible area on-screen than we've seen and move onto the next preferred - // position. - if (!fallback || fallback.overlayFit.visibleArea < overlayFit.visibleArea) { - fallback = { overlayFit, overlayPoint, originPoint, position: pos, overlayRect }; - } - } - - // If there are any positions where the overlay would fit with flexible dimensions, choose the - // one that has the greatest area available modified by the position's weight - if (flexibleFits.length) { - let bestFit: IFlexibleFit | null = null; - let bestScore = -1; - for (const fit of flexibleFits) { - const score = - fit.boundingBoxRect.width * fit.boundingBoxRect.height * (fit.position.weight || 1); - if (score > bestScore) { - bestScore = score; - bestFit = fit; - } - } - - this._isPushed = false; - this._applyPosition(bestFit!.position, bestFit!.origin); //tslint:disable-line - - return; - } - - // When none of the preferred positions fit within the viewport, take the position - // that went off-screen the least and attempt to push it on-screen. - if (this._canPush) { - this._isPushed = true; - this._applyPosition(fallback!.position, fallback!.originPoint); //tslint:disable-line - - return; - } - - // All options for getting the overlay within the viewport have been exhausted, so go with the - // position that went off-screen the least. - this._applyPosition(fallback!.position, fallback!.originPoint); //tslint:disable-line - } - - detach() { - this._resizeSubscription.unsubscribe(); - } - - /** Cleanup after the element gets destroyed. */ - dispose() { - if (!this._isDisposed) { - this.detach(); - this._boundingBox = null; - this._positionChanges.complete(); - this._isDisposed = true; - } - } - - /** - * This re-aligns the overlay element with the trigger in its last calculated position, - * even if a position higher in the "preferred positions" list would now fit. This - * allows one to re-align the panel without changing the orientation of the panel. - */ - reapplyLastPosition(): void { - if (!this._isDisposed && (!this._platform || this._platform.isBrowser)) { - this._originRect = this._origin.getBoundingClientRect(); - this._overlayRect = this._pane.getBoundingClientRect(); - this._viewportRect = this._getNarrowedViewportRect(); - - const lastPosition = this._lastPosition || this._preferredPositions[0]; - const originPoint = this._getOriginPoint(this._originRect, lastPosition); - - this._applyPosition(lastPosition, originPoint); - } - } - - /** - * Sets the list of Scrollable containers that host the origin element so that - * on reposition we can evaluate if it or the overlay has been clipped or outside view. Every - * Scrollable must be an ancestor element of the strategy's origin element. - */ - withScrollableContainers(scrollables: CdkScrollable[]) { - this.scrollables = scrollables; - } - - /** - * Adds new preferred positions. - * @param positions List of positions options for this overlay. - */ - withPositions(positions: IConnectedPosition[]): this { - this._preferredPositions = positions; - - // If the last calculated position object isn't part of the positions anymore, clear - // it in order to avoid it being picked up if the consumer tries to re-apply. - if (positions.indexOf(this._lastPosition!) === -1) { //tslint:disable-line - this._lastPosition = null; - } - - this._validatePositions(); - - return this; - } - - /** - * Sets a minimum distance the overlay may be positioned to the edge of the viewport. - * @param margin Required margin between the overlay and the viewport edge in pixels. - */ - withViewportMargin(margin: number): this { - this._viewportMargin = margin; - - return this; - } - - /** Sets whether the overlay's width and height can be constrained to fit within the viewport. */ - withFlexibleDimensions(flexibleDimensions = true): this { - this._hasFlexibleDimensions = flexibleDimensions; - - return this; - } - - /** Sets whether the overlay can grow after the initial open via flexible width/height. */ - withGrowAfterOpen(growAfterOpen = true): this { - this._growAfterOpen = growAfterOpen; - - return this; - } - - /** Sets whether the overlay can be pushed on-screen if none of the provided positions fit. */ - withPush(canPush = true): this { - this._canPush = canPush; - - return this; - } - - /** - * Sets whether the overlay's position should be locked in after it is positioned - * initially. When an overlay is locked in, it won't attempt to reposition itself - * when the position is re-applied (e.g. when the user scrolls away). - * @param isLocked Whether the overlay should locked in. - */ - withLockedPosition(isLocked = true): this { - this._positionLocked = isLocked; - - return this; - } - - /** - * Sets the origin element, relative to which to position the overlay. - * @param origin Reference to the new origin element. - */ - setOrigin(origin: ElementRef | HTMLElement): this { - this._origin = origin instanceof ElementRef ? origin.nativeElement : origin; - - return this; - } - - /** - * Sets the default offset for the overlay's connection point on the x-axis. - * @param offset New offset in the X axis. - */ - withDefaultOffsetX(offset: number): this { - this._offsetX = offset; - - return this; - } - - /** - * Sets the default offset for the overlay's connection point on the y-axis. - * @param offset New offset in the Y axis. - */ - withDefaultOffsetY(offset: number): this { - this._offsetY = offset; - - return this; - } - - /** - * Configures that the position strategy should set a `transform-origin` on some elements - * inside the overlay, depending on the current position that is being applied. This is - * useful for the cases where the origin of an animation can change depending on the - * alignment of the overlay. - * @param selector CSS selector that will be used to find the target - * elements onto which to set the transform origin. - */ - withTransformOriginOn(selector: string): this { - this._transformOriginSelector = selector; - - return this; - } - - /** - * Gets the (x, y) coordinate of a connection point on the origin based on a relative position. - */ - private _getOriginPoint(originRect: ClientRect, pos: IConnectedPosition): IPoint { - let x: number; - if (pos.originX === 'center') { - // Note: when centering we should always use the `left` - // offset, otherwise the position will be wrong in RTL. - x = originRect.left + (originRect.width / 2); //tslint:disable-line - } else { - const startX = this._isRtl() ? originRect.right : originRect.left; - const endX = this._isRtl() ? originRect.left : originRect.right; - x = pos.originX === 'start' ? startX : endX; - } - - let y: number; - if (pos.originY === 'center') { - y = originRect.top + (originRect.height / 2); //tslint:disable-line - } else { - y = pos.originY === 'top' ? originRect.top : originRect.bottom; - } - - return { x, y }; - } - - - /** - * Gets the (x, y) coordinate of the top-left corner of the overlay given a given position and - * origin point to which the overlay should be connected. - */ - private _getOverlayPoint( //tslint:disable-line - originPoint: IPoint, - overlayRect: ClientRect, - pos: IConnectedPosition): IPoint { - - // Calculate the (overlayStartX, overlayStartY), the start of the - // potential overlay position relative to the origin point. - let overlayStartX: number; - if (pos.overlayX === 'center') { - overlayStartX = -overlayRect.width / 2; //tslint:disable-line - } else if (pos.overlayX === 'start') { - overlayStartX = this._isRtl() ? -overlayRect.width : 0; - } else { - overlayStartX = this._isRtl() ? 0 : -overlayRect.width; - } - - let overlayStartY: number; - if (pos.overlayY === 'center') { - overlayStartY = -overlayRect.height / 2; //tslint:disable-line - } else { - overlayStartY = pos.overlayY === 'top' ? 0 : -overlayRect.height; - } - - // The (x, y) coordinates of the overlay. - return { - x: originPoint.x + overlayStartX, - y: originPoint.y + overlayStartY - }; - } - - /** Gets how well an overlay at the given point will fit within the viewport. */ - private _getOverlayFit(point: IPoint, overlay: ClientRect, viewport: ClientRect, - position: IConnectedPosition): IOverlayFit { - - let { x, y } = point; - let offsetX = this._getOffset(position, 'x'); //tslint:disable-line - let offsetY = this._getOffset(position, 'y'); //tslint:disable-line - - // Account for the offsets since they could push the overlay out of the viewport. - if (offsetX) { - x += offsetX; - } - - if (offsetY) { - y += offsetY; - } - - // How much the overlay would overflow at this position, on each side. - let leftOverflow = 0 - x; //tslint:disable-line - let rightOverflow = (x + overlay.width) - viewport.width; //tslint:disable-line - let topOverflow = 0 - y; //tslint:disable-line - let bottomOverflow = (y + overlay.height) - viewport.height; //tslint:disable-line - - // Visible parts of the element on each axis. - let visibleWidth = this._subtractOverflows(overlay.width, leftOverflow, rightOverflow); //tslint:disable-line - let visibleHeight = this._subtractOverflows(overlay.height, topOverflow, bottomOverflow); //tslint:disable-line - let visibleArea = visibleWidth * visibleHeight; //tslint:disable-line - - return { - visibleArea, - isCompletelyWithinViewport: (overlay.width * overlay.height) === visibleArea, - fitsInViewportVertically: visibleHeight === overlay.height, - fitsInViewportHorizontally: visibleWidth === overlay.width - }; - } - - /** - * Whether the overlay can fit within the viewport when it may resize either its width or height. - * @param fit How well the overlay fits in the viewport at some position. - * @param point The (x, y) coordinates of the overlat at some position. - * @param viewport The geometry of the viewport. - */ - private _canFitWithFlexibleDimensions(fit: IOverlayFit, point: IPoint, viewport: ClientRect) { - if (this._hasFlexibleDimensions) { - const availableHeight = viewport.bottom - point.y; - const availableWidth = viewport.right - point.x; - const minHeight = this._overlayRef.getConfig().minHeight; - const minWidth = this._overlayRef.getConfig().minWidth; - - const verticalFit = fit.fitsInViewportVertically || - (minHeight != null && minHeight <= availableHeight); - const horizontalFit = fit.fitsInViewportHorizontally || - (minWidth != null && minWidth <= availableWidth); - - return verticalFit && horizontalFit; - } - } - - /** - * Gets the point at which the overlay can be "pushed" on-screen. If the overlay is larger than - * the viewport, the top-left corner will be pushed on-screen (with overflow occuring on the - * right and bottom). - * - * @param start The starting point from which the overlay is pushed. - * @param overlay The overlay dimensions. - * @returns The point at which to position the overlay after pushing. This is effectively a new - * originPoint. - */ - private _pushOverlayOnScreen(start: IPoint, overlay: ClientRect): IPoint { - const viewport = this._viewportRect; - - // Determine how much the overlay goes outside the viewport on each side, which we'll use to - // decide which direction to push it. - const overflowRight = Math.max(start.x + overlay.width - viewport.right, 0); - const overflowBottom = Math.max(start.y + overlay.height - viewport.bottom, 0); - const overflowTop = Math.max(viewport.top - start.y, 0); - const overflowLeft = Math.max(viewport.left - start.x, 0); - - // Amount by which to push the overlay in each direction such that it remains on-screen. - let pushX, pushY = 0; //tslint:disable-line - - // If the overlay fits completely within the bounds of the viewport, push it from whichever - // direction is goes off-screen. Otherwise, push the top-left corner such that its in the - // viewport and allow for the trailing end of the overlay to go out of bounds. - if (overlay.width <= viewport.width) { - pushX = overflowLeft || -overflowRight; - } else { - pushX = viewport.left - start.x; - } - - if (overlay.height <= viewport.height) { - pushY = overflowTop || -overflowBottom; - } else { - pushY = viewport.top - start.y; - } - - return { - x: start.x + pushX, - y: start.y + pushY - }; - } - - /** - * Applies a computed position to the overlay and emits a position change. - * @param position The position preference - * @param originPoint The point on the origin element where the overlay is connected. - */ - private _applyPosition(position: IConnectedPosition, originPoint: IPoint) { - this._setTransformOrigin(position); - this._setOverlayElementStyles(originPoint, position); - this._setBoundingBoxStyles(originPoint, position); - - // Save the last connected position in case the position needs to be re-calculated. - this._lastPosition = position; - - // Notify that the position has been changed along with its change properties. - // We only emit if we've got any subscriptions, because the scroll visibility - // calculcations can be somewhat expensive. - if (this._positionChanges.observers.length) { - const scrollableViewProperties = this._getScrollVisibility(); - const changeEvent = new ConnectedOverlayPositionChange(position, scrollableViewProperties); - this._positionChanges.next(changeEvent); - } - - this._isInitialRender = false; - } - - /** Sets the transform origin based on the configured selector and the passed-in position. */ - private _setTransformOrigin(position: IConnectedPosition) { - if (!this._transformOriginSelector) { return; } - - const elements: NodeListOf = - this._boundingBox!.querySelectorAll(this._transformOriginSelector); //tslint:disable-line - let xOrigin: 'left' | 'right' | 'center'; - let yOrigin: 'top' | 'bottom' | 'center' = position.overlayY; //tslint:disable-line - - if (position.overlayX === 'center') { - xOrigin = 'center'; - } else if (this._isRtl()) { - xOrigin = position.overlayX === 'start' ? 'right' : 'left'; - } else { - xOrigin = position.overlayX === 'start' ? 'left' : 'right'; - } - - for (let i = 0; i < elements.length; i++) { //tslint:disable-line - elements[i].style.transformOrigin = `${xOrigin} ${yOrigin}`; - } - } - - /** - * Gets the position and size of the overlay's sizing container. - * - * This method does no measuring and applies no styles so that we can cheaply compute the - * bounds for all positions and choose the best fit based on these results. - */ - private _calculateBoundingBoxRect(origin: IPoint, position: IConnectedPosition): IBoundingBoxRect { - const viewport = this._viewportRect; - const isRtl = this._isRtl(); - let height, top, bottom; //tslint:disable-line - - if (position.overlayY === 'top') { - // Overlay is opening "downward" and thus is bound by the bottom viewport edge. - top = origin.y; - height = viewport.bottom - origin.y; - } else if (position.overlayY === 'bottom') { - // Overlay is opening "upward" and thus is bound by the top viewport edge. We need to add - // the viewport margin back in, because the viewport rect is narrowed down to remove the - // margin, whereas the `origin` position is calculated based on its `ClientRect`. - bottom = viewport.height - origin.y + this._viewportMargin * 2; //tslint:disable-line - height = viewport.height - bottom + this._viewportMargin; - } else { - // If neither top nor bottom, it means that the overlay - // is vertically centered on the origin point. - const smallestDistanceToViewportEdge = - Math.min(viewport.bottom - origin.y, origin.y - viewport.left); - const previousHeight = this._lastBoundingBoxSize.height; - - height = smallestDistanceToViewportEdge * 2; //tslint:disable-line - top = origin.y - smallestDistanceToViewportEdge; - - if (height > previousHeight && !this._isInitialRender && !this._growAfterOpen) { - top = origin.y - (previousHeight / 2); //tslint:disable-line - } - } - - // The overlay is opening 'right-ward' (the content flows to the right). - const isBoundedByRightViewportEdge = - (position.overlayX === 'start' && !isRtl) || - (position.overlayX === 'end' && isRtl); - - // The overlay is opening 'left-ward' (the content flows to the left). - const isBoundedByLeftViewportEdge = - (position.overlayX === 'end' && !isRtl) || - (position.overlayX === 'start' && isRtl); - - let width, left, right; //tslint:disable-line - - if (isBoundedByLeftViewportEdge) { - right = viewport.right - origin.x + this._viewportMargin; - width = origin.x - viewport.left; - } else if (isBoundedByRightViewportEdge) { - left = origin.x; - width = viewport.right - origin.x; - } else { - // If neither start nor end, it means that the overlay - // is horizontally centered on the origin point. - const smallestDistanceToViewportEdge = - Math.min(viewport.right - origin.x, origin.x - viewport.top); - const previousWidth = this._lastBoundingBoxSize.width; - - width = smallestDistanceToViewportEdge * 2; //tslint:disable-line - left = origin.x - smallestDistanceToViewportEdge; - - if (width > previousWidth && !this._isInitialRender && !this._growAfterOpen) { - left = origin.x - (previousWidth / 2); //tslint:disable-line - } - } - - return { top, left, bottom, right, width, height }; - } - - /** - * Sets the position and size of the overlay's sizing wrapper. The wrapper is positioned on the - * origin's connection point and stetches to the bounds of the viewport. - * - * @param origin The point on the origin element where the overlay is connected. - * @param position The position preference - */ - private _setBoundingBoxStyles(origin: IPoint, position: IConnectedPosition): void { - const boundingBoxRect = this._calculateBoundingBoxRect(origin, position); - - // It's weird if the overlay *grows* while scrolling, so we take the last size into account - // when applying a new size. - if (!this._isInitialRender && !this._growAfterOpen) { - boundingBoxRect.height = Math.min(boundingBoxRect.height, this._lastBoundingBoxSize.height); - boundingBoxRect.width = Math.min(boundingBoxRect.width, this._lastBoundingBoxSize.width); - } - - const styles = {} as CSSStyleDeclaration; - - if (this._hasExactPosition()) { - styles.top = styles.left = '0'; - styles.bottom = styles.right = ''; - styles.width = styles.height = '100%'; - } else { - const maxHeight = this._overlayRef.getConfig().maxHeight; - const maxWidth = this._overlayRef.getConfig().maxWidth; - - styles.height = coerceCssPixelValue(boundingBoxRect.height); - styles.top = coerceCssPixelValue(boundingBoxRect.top); - styles.bottom = coerceCssPixelValue(boundingBoxRect.bottom); - styles.width = coerceCssPixelValue(boundingBoxRect.width); - styles.left = coerceCssPixelValue(boundingBoxRect.left); - styles.right = coerceCssPixelValue(boundingBoxRect.right); - - // Push the pane content towards the proper direction. - if (position.overlayX === 'center') { - styles.alignItems = 'center'; - } else { - styles.alignItems = position.overlayX === 'end' ? 'flex-end' : 'flex-start'; - } - - if (position.overlayY === 'center') { - styles.justifyContent = 'center'; - } else { - styles.justifyContent = position.overlayY === 'bottom' ? 'flex-end' : 'flex-start'; - } - - if (maxHeight) { - styles.maxHeight = coerceCssPixelValue(maxHeight); - } - - if (maxWidth) { - styles.maxWidth = coerceCssPixelValue(maxWidth); - } - } - - this._lastBoundingBoxSize = boundingBoxRect; - - extendStyles(this._boundingBox!.style, styles); //tslint:disable-line - } - - /** Resets the styles for the bounding box so that a new positioning can be computed. */ - private _resetBoundingBoxStyles() { - extendStyles(this._boundingBox!.style, { //tslint:disable-line - top: '0', - left: '0', - right: '0', - bottom: '0', - height: '', - width: '', - alignItems: '', - justifyContent: '' - } as CSSStyleDeclaration); - } - - /** Resets the styles for the overlay pane so that a new positioning can be computed. */ - private _resetOverlayElementStyles() { - extendStyles(this._pane.style, { - top: '', - left: '', - bottom: '', - right: '', - position: '' - } as CSSStyleDeclaration); - } - - /** Sets positioning styles to the overlay element. */ - private _setOverlayElementStyles(originPoint: IPoint, position: IConnectedPosition): void { - const styles = {} as CSSStyleDeclaration; - - if (this._hasExactPosition()) { - extendStyles(styles, this._getExactOverlayY(position, originPoint)); - extendStyles(styles, this._getExactOverlayX(position, originPoint)); - } else { - styles.position = 'static'; - } - - // Use a transform to apply the offsets. We do this because the `center` positions rely on - // being in the normal flex flow and setting a `top` / `left` at all will completely throw - // off the position. We also can't use margins, because they won't have an effect in some - // cases where the element doesn't have anything to "push off of". Finally, this works - // better both with flexible and non-flexible positioning. - let transformString = ''; - let offsetX = this._getOffset(position, 'x'); //tslint:disable-line - let offsetY = this._getOffset(position, 'y'); //tslint:disable-line - - if (offsetX) { - transformString += `translateX(${offsetX}px) `; - } - - if (offsetY) { - transformString += `translateY(${offsetY}px)`; - } - - styles.transform = transformString.trim(); - - // If a maxWidth or maxHeight is specified on the overlay, we remove them. We do this because - // we need these values to both be set to "100%" for the automatic flexible sizing to work. - // The maxHeight and maxWidth are set on the boundingBox in order to enforce the constraint. - if (this._hasFlexibleDimensions && this._overlayRef.getConfig().maxHeight) { - styles.maxHeight = ''; - } - - if (this._hasFlexibleDimensions && this._overlayRef.getConfig().maxWidth) { - styles.maxWidth = ''; - } - - extendStyles(this._pane.style, styles); - } - - /** Gets the exact top/bottom for the overlay when not using flexible sizing or when pushing. */ - private _getExactOverlayY(position: IConnectedPosition, originPoint: IPoint) { - // Reset any existing styles. This is necessary in case the - // preferred position has changed since the last `apply`. - let styles = { top: null, bottom: null } as CSSStyleDeclaration; //tslint:disable-line - let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position); - - if (this._isPushed) { - overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect); - } - - // We want to set either `top` or `bottom` based on whether the overlay wants to appear - // above or below the origin and the direction in which the element will expand. - if (position.overlayY === 'bottom') { - // When using `bottom`, we adjust the y position such that it is the distance - // from the bottom of the viewport rather than the top. - const documentHeight = this._document.documentElement!.clientHeight; - styles.bottom = `${documentHeight - (overlayPoint.y + this._overlayRect.height)}px`; - } else { - styles.top = coerceCssPixelValue(overlayPoint.y); - } - - return styles; - } - - /** Gets the exact left/right for the overlay when not using flexible sizing or when pushing. */ - private _getExactOverlayX(position: IConnectedPosition, originPoint: IPoint) { - // Reset any existing styles. This is necessary in case the preferred position has - // changed since the last `apply`. - let styles = { left: null, right: null } as CSSStyleDeclaration; //tslint:disable-line - let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position); - - if (this._isPushed) { - overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect); - } - - // We want to set either `left` or `right` based on whether the overlay wants to appear "before" - // or "after" the origin, which determines the direction in which the element will expand. - // For the horizontal axis, the meaning of "before" and "after" change based on whether the - // page is in RTL or LTR. - let horizontalStyleProperty: 'left' | 'right'; - - if (this._isRtl()) { - horizontalStyleProperty = position.overlayX === 'end' ? 'left' : 'right'; - } else { - horizontalStyleProperty = position.overlayX === 'end' ? 'right' : 'left'; - } - - // When we're setting `right`, we adjust the x position such that it is the distance - // from the right edge of the viewport rather than the left edge. - if (horizontalStyleProperty === 'right') { - const documentWidth = this._document.documentElement!.clientWidth; - styles.right = `${documentWidth - (overlayPoint.x + this._overlayRect.width)}px`; - } else { - styles.left = coerceCssPixelValue(overlayPoint.x); - } - - return styles; - } - - /** - * Gets the view properties of the trigger and overlay, including whether they are clipped - * or completely outside the view of any of the strategy's scrollables. - */ - private _getScrollVisibility(): ScrollingVisibility { - // Note: needs fresh rects since the position could've changed. - const originBounds = this._origin.getBoundingClientRect(); - const overlayBounds = this._pane.getBoundingClientRect(); - - // every time, we should be able to use the scrollTop of the containers if the size of those - // containers hasn't changed. - const scrollContainerBounds = this.scrollables.map((scrollable) => { - return scrollable.getElementRef().nativeElement.getBoundingClientRect(); - }); - - return { - isOriginClipped: isElementClippedByScrolling(originBounds, scrollContainerBounds), - isOriginOutsideView: isElementScrolledOutsideView(originBounds, scrollContainerBounds), - isOverlayClipped: isElementClippedByScrolling(overlayBounds, scrollContainerBounds), - isOverlayOutsideView: isElementScrolledOutsideView(overlayBounds, scrollContainerBounds) - }; - } - - /** Subtracts the amount that an element is overflowing on an axis from it's length. */ - private _subtractOverflows(length: number, ...overflows: number[]): number { - return overflows.reduce((currentValue: number, currentOverflow: number) => { - return currentValue - Math.max(currentOverflow, 0); - }, length); - } - - /** Narrows the given viewport rect by the current _viewportMargin. */ - private _getNarrowedViewportRect(): ClientRect { - // We recalculate the viewport rect here ourselves, rather than using the ViewportRuler, - // because we want to use the `clientWidth` and `clientHeight` as the base. The difference - // being that the client properties don't include the scrollbar, as opposed to `innerWidth` - // and `innerHeight` that do. This is necessary, because the overlay container uses - // 100% `width` and `height` which don't include the scrollbar either. - const width = this._document.documentElement!.clientWidth; - const height = this._document.documentElement!.clientHeight; - const scrollPosition = this._viewportRuler.getViewportScrollPosition(); - - return { - top: scrollPosition.top + this._viewportMargin, - left: scrollPosition.left + this._viewportMargin, - right: scrollPosition.left + width - this._viewportMargin, - bottom: scrollPosition.top + height - this._viewportMargin, - width: width - (2 * this._viewportMargin), //tslint:disable-line - height: height - (2 * this._viewportMargin) //tslint:disable-line - }; - } - - /** Whether the we're dealing with an RTL context */ - private _isRtl() { - return this._overlayRef.getDirection() === 'rtl'; - } - - /** Determines whether the overlay uses exact or flexible positioning. */ - private _hasExactPosition() { - return !this._hasFlexibleDimensions || this._isPushed; - } - - /** Retrieves the offset of a position along the x or y axis. */ - private _getOffset(position: IConnectedPosition, axis: 'x' | 'y') { - if (axis === 'x') { - // We don't do something like `position['offset' + axis]` in - // order to avoid breking minifiers that rename properties. - return position.offsetX == null ? this._offsetX : position.offsetX; - } - - return position.offsetY == null ? this._offsetY : position.offsetY; - } - - /** Validates that the current position match the expected values. */ - private _validatePositions(): void { - if (!this._preferredPositions.length) { - throw Error('FlexibleConnectedPositionStrategy: At least one position is required.'); - } - - // TODO(crisbeto): remove these once Angular's template type - // checking is advanced enough to catch these cases. - this._preferredPositions.forEach((pair) => { - validateHorizontalPosition('originX', pair.originX); - validateVerticalPosition('originY', pair.originY); - validateHorizontalPosition('overlayX', pair.overlayX); - validateVerticalPosition('overlayY', pair.overlayY); - }); - } -} - -/** A simple (x, y) coordinate. */ -interface IPoint { - x: number; - y: number; -} - -/** Record of measurements for how an overlay (at a given position) fits into the viewport. */ -interface IOverlayFit { - /** Whether the overlay fits completely in the viewport. */ - isCompletelyWithinViewport: boolean; - - /** Whether the overlay fits in the viewport on the y-axis. */ - fitsInViewportVertically: boolean; - - /** Whether the overlay fits in the viewport on the x-axis. */ - fitsInViewportHorizontally: boolean; - - /** The total visible area (in px^2) of the overlay inside the viewport. */ - visibleArea: number; -} - -/** Record of the measurments determining whether an overlay will fit in a specific position. */ -interface IFallbackPosition { - position: IConnectedPosition; - originPoint: IPoint; - overlayPoint: IPoint; - overlayFit: IOverlayFit; - overlayRect: ClientRect; -} - -/** Position and size of the overlay sizing wrapper for a specific position. */ -interface IBoundingBoxRect { - top: number; - left: number; - bottom: number; - right: number; - height: number; - width: number; -} - -/** Record of measures determining how well a given position will fit with flexible dimensions. */ -interface IFlexibleFit { - position: IConnectedPosition; - origin: IPoint; - overlayRect: ClientRect; - boundingBoxRect: IBoundingBoxRect; -} - -/** A connected position as specified by the user. */ -export interface IConnectedPosition { - originX: 'start' | 'center' | 'end'; - originY: 'top' | 'center' | 'bottom'; - - overlayX: 'start' | 'center' | 'end'; - overlayY: 'top' | 'center' | 'bottom'; - - weight?: number; - offsetX?: number; - offsetY?: number; -} - -/** Shallow-extends a stylesheet object with another stylesheet object. */ -function extendStyles(dest: CSSStyleDeclaration, source: CSSStyleDeclaration): CSSStyleDeclaration { - for (let key in source) { //tslint:disable-line - if (source.hasOwnProperty(key)) { - dest[key] = source[key]; - } - } - - return dest; -} diff --git a/packages/cdk/overlay/position/global-position-strategy.spec.ts b/packages/cdk/overlay/position/global-position-strategy.spec.ts deleted file mode 100644 index a39049f81..000000000 --- a/packages/cdk/overlay/position/global-position-strategy.spec.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { NgModule, NgZone, Component } from '@angular/core'; -import { TestBed, inject } from '@angular/core/testing'; -import { PortalModule, ComponentPortal } from '@ptsecurity/cdk/portal'; -import { MockNgZone } from '@ptsecurity/cdk/testing'; - -import { OverlayModule, Overlay, OverlayConfig, OverlayRef, OverlayContainer } from '../index'; - - -describe('GlobalPositonStrategy', () => { - let overlayRef: OverlayRef; - let overlay: Overlay; - let zone: MockNgZone; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [GlobalOverlayTestModule], - providers: [{ provide: NgZone, useFactory: () => zone = new MockNgZone() }] - }); - - inject([Overlay], (o: Overlay) => { - overlay = o; - })(); - }); - - afterEach(inject([OverlayContainer], (overlayContainer: OverlayContainer) => { - if (overlayRef) { - overlayRef.dispose(); - overlayRef = null!; //tslint:disable-line - } - - overlayContainer.ngOnDestroy(); - })); - - function attachOverlay(config: OverlayConfig): OverlayRef { - const portal = new ComponentPortal(BlankPortal); - overlayRef = overlay.create(config); - overlayRef.attach(portal); - zone.simulateZoneExit(); - - return overlayRef; - } - - it('should position the element to the (top, left) with an offset', () => { - attachOverlay({ - positionStrategy: overlay.position() - .global() - .top('10px') - .left('40px') - }); - - const elementStyle = overlayRef.overlayElement.style; - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - - expect(elementStyle.marginTop).toBe('10px'); - expect(elementStyle.marginLeft).toBe('40px'); - expect(elementStyle.marginBottom).toBe(''); - expect(elementStyle.marginRight).toBe(''); - - expect(parentStyle.justifyContent).toBe('flex-start'); - expect(parentStyle.alignItems).toBe('flex-start'); - }); - - it('should position the element to the (bottom, right) with an offset', () => { - attachOverlay({ - positionStrategy: overlay.position() - .global() - .bottom('70px') - .right('15em') - }); - - const elementStyle = overlayRef.overlayElement.style; - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - - expect(elementStyle.marginTop).toBe(''); - expect(elementStyle.marginLeft).toBe(''); - expect(elementStyle.marginBottom).toBe('70px'); - expect(elementStyle.marginRight).toBe('15em'); - - expect(parentStyle.justifyContent).toBe('flex-end'); - expect(parentStyle.alignItems).toBe('flex-end'); - }); - - it('should overwrite previously applied positioning', () => { - const positionStrategy = overlay.position() - .global() - .centerHorizontally() - .centerVertically(); - - attachOverlay({ positionStrategy }); - positionStrategy.top('10px').left('40%'); - overlayRef.updatePosition(); - - const elementStyle = overlayRef.overlayElement.style; - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - - expect(elementStyle.marginTop).toBe('10px'); - expect(elementStyle.marginLeft).toBe('40%'); - expect(elementStyle.marginBottom).toBe(''); - expect(elementStyle.marginRight).toBe(''); - - expect(parentStyle.justifyContent).toBe('flex-start'); - expect(parentStyle.alignItems).toBe('flex-start'); - - positionStrategy.bottom('70px').right('15em'); - overlayRef.updatePosition(); - - expect(elementStyle.marginTop).toBe(''); - expect(elementStyle.marginLeft).toBe(''); - expect(elementStyle.marginBottom).toBe('70px'); - expect(elementStyle.marginRight).toBe('15em'); - - expect(parentStyle.justifyContent).toBe('flex-end'); - expect(parentStyle.alignItems).toBe('flex-end'); - }); - - it('should center the element', () => { - attachOverlay({ - positionStrategy: overlay.position() - .global() - .centerHorizontally() - .centerVertically() - }); - - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - - expect(parentStyle.justifyContent).toBe('center'); - expect(parentStyle.alignItems).toBe('center'); - }); - - it('should center the element with an offset', () => { - attachOverlay({ - positionStrategy: overlay.position() - .global() - .centerHorizontally('10px') - .centerVertically('15px') - }); - - const elementStyle = overlayRef.overlayElement.style; - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - - expect(elementStyle.marginLeft).toBe('10px'); - expect(elementStyle.marginTop).toBe('15px'); - - expect(parentStyle.justifyContent).toBe('center'); - expect(parentStyle.alignItems).toBe('center'); - }); - - it('should make the element position: static', () => { - attachOverlay({ - positionStrategy: overlay.position().global() - }); - - expect(overlayRef.overlayElement.style.position).toBe('static'); - }); - - it('should wrap the element in a `cdk-global-overlay-wrapper`', () => { - attachOverlay({ - positionStrategy: overlay.position().global() - }); - - const parent = overlayRef.overlayElement.parentNode as HTMLElement; - - expect(parent.classList.contains('cdk-global-overlay-wrapper')).toBe(true); - }); - - it('should remove the parent wrapper from the DOM', () => { - attachOverlay({ - positionStrategy: overlay.position().global() - }); - - const parent = overlayRef.overlayElement.parentNode!; //tslint:disable-line - - expect(document.body.contains(parent)).toBe(true); - - overlayRef.dispose(); - - expect(document.body.contains(parent)).toBe(false); - }); - - it('should set the element width', () => { - attachOverlay({ - positionStrategy: overlay.position().global().width('100px') - }); - - expect(overlayRef.overlayElement.style.width).toBe('100px'); - }); - - it('should set the element height', () => { - attachOverlay({ - positionStrategy: overlay.position().global().height('100px') - }); - - expect(overlayRef.overlayElement.style.height).toBe('100px'); - }); - - it('should reset the horizontal position and offset when the width is 100%', () => { - attachOverlay({ - positionStrategy: overlay.position() - .global() - .centerHorizontally() - .width('100%') - }); - - const elementStyle = overlayRef.overlayElement.style; - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - - expect(elementStyle.marginLeft).toBe('0px'); - expect(parentStyle.justifyContent).toBe('flex-start'); - }); - - it('should reset the vertical position and offset when the height is 100%', () => { - attachOverlay({ - positionStrategy: overlay.position() - .global() - .centerVertically() - .height('100%') - }); - - const elementStyle = overlayRef.overlayElement.style; - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - - expect(elementStyle.marginTop).toBe('0px'); - expect(parentStyle.alignItems).toBe('flex-start'); - }); - - it('should not throw when attempting to apply after the overlay has been disposed', () => { - const positionStrategy = overlay.position().global(); - - attachOverlay({ positionStrategy }); - - positionStrategy.dispose(); - - expect(() => positionStrategy.apply()).not.toThrow(); - }); - - it('should take its width and height from the overlay config', () => { - attachOverlay({ - positionStrategy: overlay.position().global(), - width: '500px', - height: '300px' - }); - - const elementStyle = overlayRef.overlayElement.style; - - expect(elementStyle.width).toBe('500px'); - expect(elementStyle.height).toBe('300px'); - }); - - it('should update the overlay size when setting it through the position strategy', () => { - attachOverlay({ - positionStrategy: overlay.position() - .global() - .width('500px') - .height('300px') - }); - - expect(overlayRef.getConfig().width).toBe('500px'); - expect(overlayRef.getConfig().height).toBe('300px'); - }); - - it('should take the dimensions from the overlay config, when they are set both in the ' + - 'config and the strategy', () => { - attachOverlay({ - positionStrategy: overlay.position().global().width('200px').height('100px'), - width: '500px', - height: '300px' - }); - - const elementStyle = overlayRef.overlayElement.style; - - expect(elementStyle.width).toBe('500px'); - expect(elementStyle.height).toBe('300px'); - }); - - it('should invert `justify-content` when using `left` in RTL', () => { - attachOverlay({ - positionStrategy: overlay.position().global().left('0'), - direction: 'rtl' - }); - - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - expect(parentStyle.justifyContent).toBe('flex-end'); - }); - - it('should invert `justify-content` when using `right` in RTL', () => { - attachOverlay({ - positionStrategy: overlay.position().global().right('0'), - direction: 'rtl' - }); - - const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style; - expect(parentStyle.justifyContent).toBe('flex-start'); - }); -}); - - -@Component({ template: '' }) -class BlankPortal { -} - -@NgModule({ - imports: [OverlayModule, PortalModule], - exports: [BlankPortal], - declarations: [BlankPortal], - entryComponents: [BlankPortal] -}) -class GlobalOverlayTestModule { -} diff --git a/packages/cdk/overlay/position/global-position-strategy.ts b/packages/cdk/overlay/position/global-position-strategy.ts deleted file mode 100644 index b2625ef7a..000000000 --- a/packages/cdk/overlay/position/global-position-strategy.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { IOverlayReference } from '../overlay-reference'; - -import { IPositionStrategy } from './position-strategy'; - - -/** - * A strategy for positioning overlays. Using this strategy, an overlay is given an - * explicit position relative to the browser's viewport. We use flexbox, instead of - * transforms, in order to avoid issues with subpixel rendering which can cause the - * element to become blurry. - */ -export class GlobalPositionStrategy implements IPositionStrategy { - /** The overlay to which this strategy is attached. */ - private _overlayRef: IOverlayReference; - private _cssPosition: string = 'static'; - private _topOffset: string = ''; - private _bottomOffset: string = ''; - private _leftOffset: string = ''; - private _rightOffset: string = ''; - private _alignItems: string = ''; - private _justifyContent: string = ''; - private _width: string = ''; - private _height: string = ''; - - attach(overlayRef: IOverlayReference): void { - const config = overlayRef.getConfig(); - - this._overlayRef = overlayRef; - - if (this._width && !config.width) { - overlayRef.updateSize({ width: this._width }); - } - - if (this._height && !config.height) { - overlayRef.updateSize({ height: this._height }); - } - - overlayRef.hostElement.classList.add('cdk-global-overlay-wrapper'); - } - - /** - * Sets the top position of the overlay. Clears any previously set vertical position. - * @param value New top offset. - */ - top(value: string = ''): this { - this._bottomOffset = ''; - this._topOffset = value; - this._alignItems = 'flex-start'; - - return this; - } - - /** - * Sets the left position of the overlay. Clears any previously set horizontal position. - * @param value New left offset. - */ - left(value: string = ''): this { - this._rightOffset = ''; - this._leftOffset = value; - this._justifyContent = 'flex-start'; - - return this; - } - - /** - * Sets the bottom position of the overlay. Clears any previously set vertical position. - * @param value New bottom offset. - */ - bottom(value: string = ''): this { - this._topOffset = ''; - this._bottomOffset = value; - this._alignItems = 'flex-end'; - - return this; - } - - /** - * Sets the right position of the overlay. Clears any previously set horizontal position. - * @param value New right offset. - */ - right(value: string = ''): this { - this._leftOffset = ''; - this._rightOffset = value; - this._justifyContent = 'flex-end'; - - return this; - } - - /** - * Sets the overlay width and clears any previously set width. - * @param value New width for the overlay - * @deprecated Pass the `width` through the `OverlayConfig`. - * @deletion-target 7.0.0 - */ - width(value: string = ''): this { - if (this._overlayRef) { - this._overlayRef.updateSize({ width: value }); - } else { - this._width = value; - } - - return this; - } - - /** - * Sets the overlay height and clears any previously set height. - * @param value New height for the overlay - * @deprecated Pass the `height` through the `OverlayConfig`. - * @deletion-target 7.0.0 - */ - height(value: string = ''): this { - if (this._overlayRef) { - this._overlayRef.updateSize({ height: value }); - } else { - this._height = value; - } - - return this; - } - - /** - * Centers the overlay horizontally with an optional offset. - * Clears any previously set horizontal position. - * - * @param offset Overlay offset from the horizontal center. - */ - centerHorizontally(offset: string = ''): this { - this.left(offset); - this._justifyContent = 'center'; - - return this; - } - - /** - * Centers the overlay vertically with an optional offset. - * Clears any previously set vertical position. - * - * @param offset Overlay offset from the vertical center. - */ - centerVertically(offset: string = ''): this { - this.top(offset); - this._alignItems = 'center'; - - return this; - } - - /** - * Apply the position to the element. - * @docs-private - */ - apply(): void { - // Since the overlay ref applies the strategy asynchronously, it could - // have been disposed before it ends up being applied. If that is the - // case, we shouldn't do anything. - if (!this._overlayRef.hasAttached()) { - return; - } - - const styles = this._overlayRef.overlayElement.style; - const parentStyles = this._overlayRef.hostElement.style; - const config = this._overlayRef.getConfig(); - - styles.position = this._cssPosition; - styles.marginLeft = config.width === '100%' ? '0' : this._leftOffset; - styles.marginTop = config.height === '100%' ? '0' : this._topOffset; - styles.marginBottom = this._bottomOffset; - styles.marginRight = this._rightOffset; - - if (config.width === '100%') { - parentStyles.justifyContent = 'flex-start'; - } else if (this._overlayRef.getConfig().direction === 'rtl') { - // In RTL the browser will invert `flex-start` and `flex-end` automatically, but we - // don't want that because our positioning is explicitly `left` and `right`, hence - // why we do another inversion to ensure that the overlay stays in the same position. - if (this._justifyContent === 'flex-start') { - parentStyles.justifyContent = 'flex-end'; - } else if (this._justifyContent === 'flex-end') { - parentStyles.justifyContent = 'flex-start'; - } - } else { - parentStyles.justifyContent = this._justifyContent; - } - - parentStyles.alignItems = config.height === '100%' ? 'flex-start' : this._alignItems; - } - - /** - * Noop implemented as a part of the IPositionStrategy interface. - * @docs-private - */ - dispose(): void { - } //tslint:disable-line -} diff --git a/packages/cdk/overlay/position/overlay-position-builder.ts b/packages/cdk/overlay/position/overlay-position-builder.ts deleted file mode 100644 index 649c5cc33..000000000 --- a/packages/cdk/overlay/position/overlay-position-builder.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { DOCUMENT } from '@angular/common'; -import { ElementRef, Inject, Injectable, Optional } from '@angular/core'; -import { Platform } from '@ptsecurity/cdk/platform'; -import { ViewportRuler } from '@ptsecurity/cdk/scrolling'; - -import { IOriginConnectionPosition, IOverlayConnectionPosition } from './connected-position'; -import { ConnectedPositionStrategy } from './connected-position-strategy'; -import { FlexibleConnectedPositionStrategy } from './flexible-connected-position-strategy'; -import { GlobalPositionStrategy } from './global-position-strategy'; - - -/** Builder for overlay position strategy. */ -@Injectable({ providedIn: 'root' }) -export class OverlayPositionBuilder { - constructor( - private _viewportRuler: ViewportRuler, - @Inject(DOCUMENT) private _document: any, - // @deletion-target 7.0.0 `_platform` parameter to be made required. - @Optional() private _platform?: Platform) { - } - - /** - * Creates a global position strategy. - */ - global(): GlobalPositionStrategy { - return new GlobalPositionStrategy(); - } - - /** - * Creates a relative position strategy. - * @param elementRef //tslint:disable-line - * @param originPos //tslint:disable-line - * @param overlayPos //tslint:disable-line - * @deprecated Use `flexibleConnectedTo` instead. - * @deletion-target 7.0.0 - */ - connectedTo( - elementRef: ElementRef, - originPos: IOriginConnectionPosition, - overlayPos: IOverlayConnectionPosition): ConnectedPositionStrategy { //tslint:disable-line - - return new ConnectedPositionStrategy(originPos, overlayPos, elementRef, this._viewportRuler, //tslint:disable-line - this._document); - } - - /** - * Creates a flexible position strategy. - * @param elementRef //tslint:disable-line - */ - flexibleConnectedTo(elementRef: ElementRef | HTMLElement): FlexibleConnectedPositionStrategy { - return new FlexibleConnectedPositionStrategy(elementRef, this._viewportRuler, this._document, - this._platform); - } - -} diff --git a/packages/cdk/overlay/position/position-strategy.ts b/packages/cdk/overlay/position/position-strategy.ts deleted file mode 100644 index 0bb6811e4..000000000 --- a/packages/cdk/overlay/position/position-strategy.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IOverlayReference } from '../overlay-reference'; - - -/** Strategy for setting the position on an overlay. */ -export interface IPositionStrategy { - /** Attaches this position strategy to an overlay. */ - attach(overlayRef: IOverlayReference): void; - - /** Updates the position of the overlay element. */ - apply(): void; - - /** Called when the overlay is detached. */ - detach?(): void; - - /** Cleans up any DOM modifications made by the position strategy, if necessary. */ - dispose(): void; -} diff --git a/packages/cdk/overlay/position/scroll-clip.ts b/packages/cdk/overlay/position/scroll-clip.ts deleted file mode 100644 index 2d6a4389e..000000000 --- a/packages/cdk/overlay/position/scroll-clip.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Gets whether an element is scrolled outside of view by any of its parent scrolling containers. - * @param element Dimensions of the element (from getBoundingClientRect) - * @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect) - * @returns Whether the element is scrolled out of view - * @docs-private - */ -export function isElementScrolledOutsideView(element: ClientRect, scrollContainers: ClientRect[]) { - return scrollContainers.some((containerBounds) => { - const outsideAbove = element.bottom < containerBounds.top; - const outsideBelow = element.top > containerBounds.bottom; - const outsideLeft = element.right < containerBounds.left; - const outsideRight = element.left > containerBounds.right; - - return outsideAbove || outsideBelow || outsideLeft || outsideRight; - }); -} - - -/** - * Gets whether an element is clipped by any of its scrolling containers. - * @param element Dimensions of the element (from getBoundingClientRect) - * @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect) - * @returns Whether the element is clipped - * @docs-private - */ -export function isElementClippedByScrolling(element: ClientRect, scrollContainers: ClientRect[]) { - return scrollContainers.some((scrollContainerRect) => { - const clippedAbove = element.top < scrollContainerRect.top; - const clippedBelow = element.bottom > scrollContainerRect.bottom; - const clippedLeft = element.left < scrollContainerRect.left; - const clippedRight = element.right > scrollContainerRect.right; - - return clippedAbove || clippedBelow || clippedLeft || clippedRight; - }); -} diff --git a/packages/cdk/overlay/public-api.ts b/packages/cdk/overlay/public-api.ts deleted file mode 100644 index 862dcbfab..000000000 --- a/packages/cdk/overlay/public-api.ts +++ /dev/null @@ -1,23 +0,0 @@ -export * from './overlay-config'; -export * from './position/connected-position'; -export * from './scroll/index'; -export * from './overlay-module'; -export { Overlay } from './overlay'; -export { OverlayContainer } from './overlay-container'; -export { CdkOverlayOrigin, CdkConnectedOverlay } from './overlay-directives'; -export { FullscreenOverlayContainer } from './fullscreen-overlay-container'; -export { OverlayRef, IOverlaySizeConfig } from './overlay-ref'; -export { ViewportRuler } from '@ptsecurity/cdk/scrolling'; -export { IComponentType } from '@ptsecurity/cdk/portal'; -export { OverlayKeyboardDispatcher } from './keyboard/overlay-keyboard-dispatcher'; -export { OverlayPositionBuilder } from './position/overlay-position-builder'; - -// Export pre-defined position strategies and interface to build custom ones. -export { IPositionStrategy } from './position/position-strategy'; -export { GlobalPositionStrategy } from './position/global-position-strategy'; -export { ConnectedPositionStrategy } from './position/connected-position-strategy'; -export { - IConnectedPosition, - FlexibleConnectedPositionStrategy -} from './position/flexible-connected-position-strategy'; -export { VIEWPORT_RULER_PROVIDER } from '@ptsecurity/cdk/scrolling'; diff --git a/packages/cdk/overlay/scroll/block-scroll-strategy.spec.ts b/packages/cdk/overlay/scroll/block-scroll-strategy.spec.ts deleted file mode 100644 index 3f687e9b3..000000000 --- a/packages/cdk/overlay/scroll/block-scroll-strategy.spec.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { Component, NgModule } from '@angular/core'; -import { async, inject, TestBed } from '@angular/core/testing'; - -import { Platform } from '@ptsecurity/cdk/platform'; -import { ComponentPortal, PortalModule } from '@ptsecurity/cdk/portal'; -import { ViewportRuler } from '@ptsecurity/cdk/scrolling'; - -import { Overlay, OverlayContainer, OverlayModule, OverlayRef, OverlayConfig } from '../index'; - - -/* tslint:disable:no-magic-numbers */ -describe('BlockScrollStrategy', () => { - let platform: Platform; - let viewport: ViewportRuler; - let documentElement: HTMLElement; - let overlayRef: OverlayRef; - let componentPortal: ComponentPortal; - let forceScrollElement: HTMLElement; - - beforeEach(async(() => { - documentElement = document.documentElement!; - - // Ensure a clean state for every test. - documentElement.classList.remove('cdk-global-scrollblock'); - - TestBed.configureTestingModule({ - imports: [OverlayModule, PortalModule, OverlayTestModule] - }).compileComponents(); - })); - - beforeEach(inject([Overlay, ViewportRuler, Platform], - (overlay: Overlay, viewportRuler: ViewportRuler, _platform: Platform) => { - const overlayConfig = new OverlayConfig({scrollStrategy: overlay.scrollStrategies.block()}); - - overlayRef = overlay.create(overlayConfig); - componentPortal = new ComponentPortal(FocacciaMsg); - - viewport = viewportRuler; - forceScrollElement = document.createElement('div'); - document.body.appendChild(forceScrollElement); - forceScrollElement.style.width = '100px'; - forceScrollElement.style.height = '3000px'; - forceScrollElement.style.background = 'rebeccapurple'; - platform = _platform; - })); - - afterEach(inject([OverlayContainer], (container: OverlayContainer) => { - overlayRef.dispose(); - document.body.removeChild(forceScrollElement); - window.scroll(0, 0); - container.getContainerElement().parentNode!.removeChild(container.getContainerElement()); - })); - - it('should toggle scroll blocking along the y axis', skipIOS(() => { - window.scroll(0, 100); - expect(viewport.getViewportScrollPosition().top) - .toBe(100, 'Expected viewport to be scrollable initially.'); - - overlayRef.attach(componentPortal); - expect(documentElement.style.top) - .toBe('-100px', 'Expected element to be offset by the previous scroll amount.'); - - window.scroll(0, 300); - expect(viewport.getViewportScrollPosition().top) - .toBe(100, 'Expected the viewport not to scroll.'); - - overlayRef.detach(); - expect(viewport.getViewportScrollPosition().top) - .toBe(100, 'Expected old scroll position to have bee restored after disabling.'); - - window.scroll(0, 300); - expect(viewport.getViewportScrollPosition().top) - .toBe(300, 'Expected user to be able to scroll after disabling.'); - })); - - - it('should toggle scroll blocking along the x axis', skipIOS(() => { - forceScrollElement.style.height = '100px'; - forceScrollElement.style.width = '3000px'; - - window.scroll(100, 0); - expect(viewport.getViewportScrollPosition().left) - .toBe(100, 'Expected viewport to be scrollable initially.'); - - overlayRef.attach(componentPortal); - expect(documentElement.style.left) - .toBe('-100px', 'Expected element to be offset by the previous scroll amount.'); - - window.scroll(300, 0); - expect(viewport.getViewportScrollPosition().left) - .toBe(100, 'Expected the viewport not to scroll.'); - - overlayRef.detach(); - expect(viewport.getViewportScrollPosition().left) - .toBe(100, 'Expected old scroll position to have bee restored after disabling.'); - - window.scroll(300, 0); - expect(viewport.getViewportScrollPosition().left) - .toBe(300, 'Expected user to be able to scroll after disabling.'); - })); - - - it('should toggle the `cdk-global-scrollblock` class', skipIOS(() => { - expect(documentElement.classList).not.toContain('cdk-global-scrollblock'); - - overlayRef.attach(componentPortal); - expect(documentElement.classList).toContain('cdk-global-scrollblock'); - - overlayRef.detach(); - expect(documentElement.classList).not.toContain('cdk-global-scrollblock'); - })); - - it('should restore any previously-set inline styles', skipIOS(() => { - const root = documentElement; - - root.style.top = '13px'; - root.style.left = '37px'; - - overlayRef.attach(componentPortal); - - expect(root.style.top).not.toBe('13px'); - expect(root.style.left).not.toBe('37px'); - - overlayRef.detach(); - - expect(root.style.top).toBe('13px'); - expect(root.style.left).toBe('37px'); - })); - - it(`should't do anything if the page isn't scrollable`, skipIOS(() => { - forceScrollElement.style.display = 'none'; - overlayRef.attach(componentPortal); - expect(documentElement.classList).not.toContain('cdk-global-scrollblock'); - })); - - - it('should keep the content width', () => { - forceScrollElement.style.width = '100px'; - - const previousContentWidth = documentElement.getBoundingClientRect().width; - - overlayRef.attach(componentPortal); - - expect(documentElement.getBoundingClientRect().width).toBe(previousContentWidth); - }); - - it('should not clobber user-defined scroll-behavior', skipIOS(() => { - const root = documentElement; - const body = document.body; - - root.style['scrollBehavior'] = body.style['scrollBehavior'] = 'smooth'; - - // Get the value via the style declaration in order to - // handle browsers that don't support the property yet. - const initialRootValue = root.style['scrollBehavior']; - const initialBodyValue = root.style['scrollBehavior']; - - overlayRef.attach(componentPortal); - overlayRef.detach(); - - expect(root.style['scrollBehavior']).toBe(initialRootValue); - expect(body.style['scrollBehavior']).toBe(initialBodyValue); - - // Avoid bleeding styles into other tests. - root.style['scrollBehavior'] = body.style['scrollBehavior'] = ''; - })); - - /** - * Skips the specified test, if it is being executed on iOS. This is necessary, because - * programmatic scrolling inside the Karma iframe doesn't work on iOS, which renders these - * tests unusable. For example, something as basic as the following won't work: - * ``` - * window.scroll(0, 100); - * expect(viewport.getViewportScrollPosition().top).toBe(100); - * ``` - * @param spec Test to be executed or skipped. - */ - function skipIOS(spec: Function) { - return () => { - if (!platform.IOS) { - spec(); - } - }; - } - -}); - - -/** Simple component that we can attach to the overlay. */ -@Component({template: '

Focaccia

'}) -class FocacciaMsg { } - - -/** Test module to hold the component. */ -@NgModule({ - imports: [OverlayModule, PortalModule], - declarations: [FocacciaMsg], - entryComponents: [FocacciaMsg] -}) -class OverlayTestModule { } diff --git a/packages/cdk/overlay/scroll/block-scroll-strategy.ts b/packages/cdk/overlay/scroll/block-scroll-strategy.ts deleted file mode 100644 index 11876dab5..000000000 --- a/packages/cdk/overlay/scroll/block-scroll-strategy.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { coerceCssPixelValue } from '@ptsecurity/cdk/coercion'; -import { ViewportRuler } from '@ptsecurity/cdk/scrolling'; - -import { IScrollStrategy } from './scroll-strategy'; - - -/** - * Strategy that will prevent the user from scrolling while the overlay is visible. - */ -export class BlockScrollStrategy implements IScrollStrategy { - private _previousHTMLStyles = { top: '', left: '' }; - private _previousScrollPosition: { top: number, left: number }; - private _isEnabled = false; - private _document: Document; - - constructor(private _viewportRuler: ViewportRuler, document: any) { - this._document = document; - } - - /** Attaches this scroll strategy to an overlay. */ - attach() { - } // tslint:disable-line - - /** Blocks page-level scroll while the attached overlay is open. */ - enable() { // tslint:disable-line - - if (this._canBeEnabled()) { - const root = this._document.documentElement!; - - this._previousScrollPosition = this._viewportRuler.getViewportScrollPosition(); - - // Cache the previous inline styles in case the user had set them. - this._previousHTMLStyles.left = root.style.left || ''; - this._previousHTMLStyles.top = root.style.top || ''; - - // Note: we're using the `html` node, instead of the `body`, because the `body` may - // have the user agent margin, whereas the `html` is guaranteed not to have one. - root.style.left = coerceCssPixelValue(-this._previousScrollPosition.left); - root.style.top = coerceCssPixelValue(-this._previousScrollPosition.top); - root.classList.add('cdk-global-scrollblock'); - this._isEnabled = true; - } - } - - /** Unblocks page-level scroll while the attached overlay is open. */ - disable() { - if (this._isEnabled) { - const html = this._document.documentElement!; - const body = this._document.body!; - const previousHtmlScrollBehavior = html.style['scrollBehavior'] || ''; // tslint:disable-line - const previousBodyScrollBehavior = body.style['scrollBehavior'] || ''; // tslint:disable-line - - this._isEnabled = false; - - html.style.left = this._previousHTMLStyles.left; - html.style.top = this._previousHTMLStyles.top; - html.classList.remove('cdk-global-scrollblock'); - - // Disable user-defined smooth scrolling temporarily while we restore the scroll position. - // See https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior - html.style['scrollBehavior'] = body.style['scrollBehavior'] = 'auto'; // tslint:disable-line - - window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top); - - html.style['scrollBehavior'] = previousHtmlScrollBehavior; // tslint:disable-line - body.style['scrollBehavior'] = previousBodyScrollBehavior; // tslint:disable-line - } - } - - private _canBeEnabled(): boolean { - // Since the scroll strategies can't be singletons, we have to use a global CSS class - // (`cdk-global-scrollblock`) to make sure that we don't try to disable global - // scrolling multiple times. - const html = this._document.documentElement!; - - if (html.classList.contains('cdk-global-scrollblock') || this._isEnabled) { - return false; - } - - const body = this._document.body; - const viewport = this._viewportRuler.getViewportSize(); - - return body.scrollHeight > viewport.height || body.scrollWidth > viewport.width; - } -} diff --git a/packages/cdk/overlay/scroll/close-scroll-strategy.spec.ts b/packages/cdk/overlay/scroll/close-scroll-strategy.spec.ts deleted file mode 100644 index 2feab076a..000000000 --- a/packages/cdk/overlay/scroll/close-scroll-strategy.spec.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { NgModule, Component, NgZone } from '@angular/core'; -import { inject, TestBed, fakeAsync } from '@angular/core/testing'; -import { ComponentPortal, PortalModule } from '@ptsecurity/cdk/portal'; -import { ScrollDispatcher, ViewportRuler } from '@ptsecurity/cdk/scrolling'; -import { Subject } from 'rxjs'; - -import { - Overlay, - OverlayConfig, - OverlayRef, - OverlayModule, - OverlayContainer -} from '../index'; - - -describe('CloseScrollStrategy', () => { - let overlayRef: OverlayRef; - let componentPortal: ComponentPortal; - let scrolledSubject = new Subject(); // tslint:disable-line - let scrollPosition: number; - - beforeEach(fakeAsync(() => { - scrollPosition = 0; - - TestBed.configureTestingModule({ - imports: [OverlayModule, PortalModule, OverlayTestModule], - providers: [ - { - provide: ScrollDispatcher, useFactory: () => ({ - scrolled: () => scrolledSubject.asObservable() - }) - }, - { - provide: ViewportRuler, useFactory: () => ({ - getViewportScrollPosition: () => ({ top: scrollPosition }) - }) - } - ] - }); - - TestBed.compileComponents(); - })); - - beforeEach(inject([Overlay], (overlay: Overlay) => { - let overlayConfig = new OverlayConfig({ scrollStrategy: overlay.scrollStrategies.close() }); // tslint:disable-line - overlayRef = overlay.create(overlayConfig); - componentPortal = new ComponentPortal(MozarellaMsg); - })); - - afterEach(inject([OverlayContainer], (container: OverlayContainer) => { - overlayRef.dispose(); - container.getContainerElement().parentNode!.removeChild(container.getContainerElement()); // tslint:disable-line - })); - - it('should detach the overlay as soon as the user scrolls', () => { - overlayRef.attach(componentPortal); - spyOn(overlayRef, 'detach'); - - scrolledSubject.next(); - expect(overlayRef.detach).toHaveBeenCalled(); - }); - - it('should not attempt to detach the overlay after it has been detached', () => { - overlayRef.attach(componentPortal); - overlayRef.detach(); - - spyOn(overlayRef, 'detach'); - scrolledSubject.next(); - - expect(overlayRef.detach).not.toHaveBeenCalled(); - }); - - it('should detach inside the NgZone', () => { - const spy = jasmine.createSpy('detachment spy'); - const subscription = overlayRef.detachments().subscribe(() => spy(NgZone.isInAngularZone())); - - overlayRef.attach(componentPortal); - scrolledSubject.next(); - - expect(spy).toHaveBeenCalledWith(true); - subscription.unsubscribe(); - }); - - it('should be able to reposition the overlay up to a certain threshold before closing', - inject([Overlay], (overlay: Overlay) => { - overlayRef.dispose(); - - overlayRef = overlay.create({ - scrollStrategy: overlay.scrollStrategies.close({ threshold: 50 }) - }); - - overlayRef.attach(componentPortal); - spyOn(overlayRef, 'updatePosition'); - spyOn(overlayRef, 'detach'); - - for (let i = 0; i < 50; i++) { // tslint:disable-line - scrollPosition++; - scrolledSubject.next(); - } - - expect(overlayRef.updatePosition).toHaveBeenCalledTimes(50); // tslint:disable-line - expect(overlayRef.detach).not.toHaveBeenCalled(); - - scrollPosition++; - scrolledSubject.next(); - - expect(overlayRef.detach).toHaveBeenCalledTimes(1); - })); - - it('should not close if the user starts scrolling away and comes back', - inject([Overlay], (overlay: Overlay) => { - overlayRef.dispose(); - scrollPosition = 100; // tslint:disable-line - - overlayRef = overlay.create({ - scrollStrategy: overlay.scrollStrategies.close({ threshold: 50 }) - }); - - overlayRef.attach(componentPortal); - spyOn(overlayRef, 'updatePosition'); - spyOn(overlayRef, 'detach'); - - // Scroll down 30px. - for (let i = 0; i < 30; i++) { // tslint:disable-line - scrollPosition++; - scrolledSubject.next(); - } - - // Scroll back up 30px. - for (let i = 0; i < 30; i++) { // tslint:disable-line - scrollPosition--; - scrolledSubject.next(); - } - - expect(overlayRef.updatePosition).toHaveBeenCalledTimes(60); // tslint:disable-line - expect(overlayRef.detach).not.toHaveBeenCalled(); - })); -}); - - -/** Simple component that we can attach to the overlay. */ -@Component({ template: '

Mozarella

' }) -class MozarellaMsg { -} - - -/** Test module to hold the component. */ -@NgModule({ - imports: [OverlayModule, PortalModule], - declarations: [MozarellaMsg], - entryComponents: [MozarellaMsg] -}) -class OverlayTestModule { -} diff --git a/packages/cdk/overlay/scroll/close-scroll-strategy.ts b/packages/cdk/overlay/scroll/close-scroll-strategy.ts deleted file mode 100644 index 4dda2af56..000000000 --- a/packages/cdk/overlay/scroll/close-scroll-strategy.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { NgZone } from '@angular/core'; -import { ScrollDispatcher, ViewportRuler } from '@ptsecurity/cdk/scrolling'; -import { Subscription } from 'rxjs'; - -import { IOverlayReference } from '../overlay-reference'; - -import { IScrollStrategy, getMatScrollStrategyAlreadyAttachedError } from './scroll-strategy'; - - -/** - * Config options for the CloseScrollStrategy. - */ -export interface ICloseScrollStrategyConfig { - /** Amount of pixels the user has to scroll before the overlay is closed. */ - threshold?: number; -} - -/** - * Strategy that will close the overlay as soon as the user starts scrolling. - */ -export class CloseScrollStrategy implements IScrollStrategy { - private _scrollSubscription: Subscription | null = null; - private _overlayRef: IOverlayReference; - private _initialScrollPosition: number; - - constructor( - private _scrollDispatcher: ScrollDispatcher, - private _ngZone: NgZone, - private _viewportRuler: ViewportRuler, - private _config?: ICloseScrollStrategyConfig) { - } - - /** Attaches this scroll strategy to an overlay. */ - attach(overlayRef: IOverlayReference) { - if (this._overlayRef) { - throw getMatScrollStrategyAlreadyAttachedError(); - } - - this._overlayRef = overlayRef; - } - - /** Enables the closing of the attached overlay on scroll. */ - enable() { - if (this._scrollSubscription) { - return; - } - - const stream = this._scrollDispatcher.scrolled(0); - - if (this._config && this._config.threshold && this._config.threshold > 1) { - this._initialScrollPosition = this._viewportRuler.getViewportScrollPosition().top; - - this._scrollSubscription = stream.subscribe(() => { - const scrollPosition = this._viewportRuler.getViewportScrollPosition().top; - - if (Math.abs(scrollPosition - this._initialScrollPosition) > this._config!.threshold!) { //tslint:disable-line - this._detach(); - } else { - this._overlayRef.updatePosition(); - } - }); - } else { - this._scrollSubscription = stream.subscribe(this._detach); - } - } - - /** Disables the closing the attached overlay on scroll. */ - disable() { - if (this._scrollSubscription) { - this._scrollSubscription.unsubscribe(); - this._scrollSubscription = null; - } - } - - /** Detaches the overlay ref and disables the scroll strategy. */ - private _detach = () => { - this.disable(); - - if (this._overlayRef.hasAttached()) { - this._ngZone.run(() => this._overlayRef.detach()); - } - } -} diff --git a/packages/cdk/overlay/scroll/index.ts b/packages/cdk/overlay/scroll/index.ts deleted file mode 100644 index 0ae16a51d..000000000 --- a/packages/cdk/overlay/scroll/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export { CdkScrollable, ScrollDispatcher } from '@ptsecurity/cdk/scrolling'; - -// Export pre-defined scroll strategies and interface to build custom ones. -export { IScrollStrategy } from './scroll-strategy'; -export { ScrollStrategyOptions } from './scroll-strategy-options'; -export { - RepositionScrollStrategy, - IRepositionScrollStrategyConfig -} from './reposition-scroll-strategy'; -export { CloseScrollStrategy } from './close-scroll-strategy'; -export { NoopScrollStrategy } from './noop-scroll-strategy'; -export { BlockScrollStrategy } from './block-scroll-strategy'; diff --git a/packages/cdk/overlay/scroll/noop-scroll-strategy.ts b/packages/cdk/overlay/scroll/noop-scroll-strategy.ts deleted file mode 100644 index c5e67df5d..000000000 --- a/packages/cdk/overlay/scroll/noop-scroll-strategy.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IScrollStrategy } from './scroll-strategy'; - - -/** Scroll strategy that doesn't do anything. */ -export class NoopScrollStrategy implements IScrollStrategy { - /** Does nothing, as this scroll strategy is a no-op. */ - enable() { - } // tslint:disable-line - /** Does nothing, as this scroll strategy is a no-op. */ - disable() { - } // tslint:disable-line - /** Does nothing, as this scroll strategy is a no-op. */ - attach() { - } // tslint:disable-line -} diff --git a/packages/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts b/packages/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts deleted file mode 100644 index 37f700405..000000000 --- a/packages/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Component, NgModule } from '@angular/core'; -import { async, inject, TestBed } from '@angular/core/testing'; -import { ComponentPortal, PortalModule } from '@ptsecurity/cdk/portal'; -import { Subject } from 'rxjs'; - -import { - Overlay, - OverlayContainer, - OverlayModule, - OverlayRef, - OverlayConfig, - ScrollDispatcher -} from '../index'; - - -describe('RepositionScrollStrategy', () => { - let overlayRef: OverlayRef; - let overlay: Overlay; - let componentPortal: ComponentPortal; - let scrolledSubject = new Subject(); // tslint:disable-line - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [OverlayModule, PortalModule, OverlayTestModule], - providers: [ - { - provide: ScrollDispatcher, useFactory: () => ({ - scrolled: () => scrolledSubject.asObservable() - }) - } - ] - }); - - TestBed.compileComponents(); - })); - - beforeEach(inject([Overlay], (o: Overlay) => { - overlay = o; - componentPortal = new ComponentPortal(PastaMsg); - })); - - afterEach(inject([OverlayContainer], (container: OverlayContainer) => { - overlayRef.dispose(); - container.getContainerElement().parentNode!.removeChild(container.getContainerElement()); // tslint:disable-line - })); - - it('should update the overlay position when the page is scrolled', () => { - const overlayConfig = new OverlayConfig({ - scrollStrategy: overlay.scrollStrategies.reposition() - }); - - overlayRef = overlay.create(overlayConfig); - overlayRef.attach(componentPortal); - spyOn(overlayRef, 'updatePosition'); - - scrolledSubject.next(); - expect(overlayRef.updatePosition).toHaveBeenCalledTimes(1); - - scrolledSubject.next(); - expect(overlayRef.updatePosition).toHaveBeenCalledTimes(2); // tslint:disable-line - }); - - it('should not be updating the position after the overlay is detached', () => { - const overlayConfig = new OverlayConfig({ - scrollStrategy: overlay.scrollStrategies.reposition() - }); - - overlayRef = overlay.create(overlayConfig); - overlayRef.attach(componentPortal); - spyOn(overlayRef, 'updatePosition'); - - overlayRef.detach(); - scrolledSubject.next(); - - expect(overlayRef.updatePosition).not.toHaveBeenCalled(); - }); - - it('should not be updating the position after the overlay is destroyed', () => { - const overlayConfig = new OverlayConfig({ - scrollStrategy: overlay.scrollStrategies.reposition() - }); - - overlayRef = overlay.create(overlayConfig); - overlayRef.attach(componentPortal); - spyOn(overlayRef, 'updatePosition'); - - overlayRef.dispose(); - scrolledSubject.next(); - - expect(overlayRef.updatePosition).not.toHaveBeenCalled(); - }); - - it('should be able to close the overlay once it is out of view', () => { - let overlayConfig = new OverlayConfig({ //tslint:disable-line - scrollStrategy: overlay.scrollStrategies.reposition({ - autoClose: true - }) - }); - - overlayRef = overlay.create(overlayConfig); - overlayRef.attach(componentPortal); - spyOn(overlayRef, 'updatePosition'); - spyOn(overlayRef, 'detach'); - spyOn(overlayRef.overlayElement, 'getBoundingClientRect').and.returnValue({ - top: -1000, - bottom: -900, - left: 0, - right: 100, - width: 100, - height: 100 - }); - - scrolledSubject.next(); - expect(overlayRef.detach).toHaveBeenCalledTimes(1); - }); - -}); - - -/** Simple component that we can attach to the overlay. */ -@Component({ template: '

Pasta

' }) -class PastaMsg { -} - - -/** Test module to hold the component. */ -@NgModule({ - imports: [OverlayModule, PortalModule], - declarations: [PastaMsg], - entryComponents: [PastaMsg] -}) -class OverlayTestModule { -} diff --git a/packages/cdk/overlay/scroll/reposition-scroll-strategy.ts b/packages/cdk/overlay/scroll/reposition-scroll-strategy.ts deleted file mode 100644 index 9d5afde94..000000000 --- a/packages/cdk/overlay/scroll/reposition-scroll-strategy.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { NgZone } from '@angular/core'; -import { ScrollDispatcher, ViewportRuler } from '@ptsecurity/cdk/scrolling'; -import { Subscription } from 'rxjs'; - -import { IOverlayReference } from '../overlay-reference'; -import { isElementScrolledOutsideView } from '../position/scroll-clip'; - -import { IScrollStrategy, getMatScrollStrategyAlreadyAttachedError } from './scroll-strategy'; - - -/** - * Config options for the RepositionScrollStrategy. - */ -export interface IRepositionScrollStrategyConfig { - /** Time in milliseconds to throttle the scroll events. */ - scrollThrottle?: number; - - /** Whether to close the overlay once the user has scrolled away completely. */ - autoClose?: boolean; -} - -/** - * Strategy that will update the element position as the user is scrolling. - */ -export class RepositionScrollStrategy implements IScrollStrategy { - private _scrollSubscription: Subscription | null = null; - private _overlayRef: IOverlayReference; - - constructor( - private _scrollDispatcher: ScrollDispatcher, - private _viewportRuler: ViewportRuler, - private _ngZone: NgZone, - private _config?: IRepositionScrollStrategyConfig) { - } - - /** Attaches this scroll strategy to an overlay. */ - attach(overlayRef: IOverlayReference) { - if (this._overlayRef) { - throw getMatScrollStrategyAlreadyAttachedError(); - } - - this._overlayRef = overlayRef; - } - - /** Enables repositioning of the attached overlay on scroll. */ - enable() { - if (!this._scrollSubscription) { - const throttle = this._config ? this._config.scrollThrottle : 0; - - this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe(() => { - this._overlayRef.updatePosition(); - - // TODO(crisbeto): make `close` on by default once all components can handle it. - if (this._config && this._config.autoClose) { - const overlayRect = this._overlayRef.overlayElement.getBoundingClientRect(); - const { width, height } = this._viewportRuler.getViewportSize(); - - // TODO(crisbeto): include all ancestor scroll containers here once - // we have a way of exposing the trigger element to the scroll strategy. - const parentRects = [{ width, height, bottom: height, right: width, top: 0, left: 0 }]; - - if (isElementScrolledOutsideView(overlayRect, parentRects)) { - this.disable(); - this._ngZone.run(() => this._overlayRef.detach()); - } - } - }); - } - } - - /** Disables repositioning of the attached overlay on scroll. */ - disable() { - if (this._scrollSubscription) { - this._scrollSubscription.unsubscribe(); - this._scrollSubscription = null; - } - } -} diff --git a/packages/cdk/overlay/scroll/scroll-strategy-options.ts b/packages/cdk/overlay/scroll/scroll-strategy-options.ts deleted file mode 100644 index a79bc31e6..000000000 --- a/packages/cdk/overlay/scroll/scroll-strategy-options.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable, NgZone } from '@angular/core'; -import { ScrollDispatcher, ViewportRuler } from '@ptsecurity/cdk/scrolling'; - - -import { BlockScrollStrategy } from './block-scroll-strategy'; -import { CloseScrollStrategy, ICloseScrollStrategyConfig } from './close-scroll-strategy'; -import { NoopScrollStrategy } from './noop-scroll-strategy'; -import { - RepositionScrollStrategy, - IRepositionScrollStrategyConfig -} from './reposition-scroll-strategy'; - - -/** - * Options for how an overlay will handle scrolling. - * - * Users can provide a custom value for `ScrollStrategyOptions` to replace the default - * behaviors. This class primarily acts as a factory for ScrollStrategy instances. - */ -@Injectable({ providedIn: 'root' }) -export class ScrollStrategyOptions { - private _document: Document; - - constructor( - private _scrollDispatcher: ScrollDispatcher, - private _viewportRuler: ViewportRuler, - private _ngZone: NgZone, - @Inject(DOCUMENT) document: any) { - this._document = document; - } - - /** Do nothing on scroll. */ - noop = () => new NoopScrollStrategy(); - - /** - * Close the overlay as soon as the user scrolls. - * @param config Configuration to be used inside the scroll strategy. - */ - close = (config?: ICloseScrollStrategyConfig) => new CloseScrollStrategy(this._scrollDispatcher, - this._ngZone, this._viewportRuler, config) - - /** Block scrolling. */ - block = () => new BlockScrollStrategy(this._viewportRuler, this._document); - - /** - * Update the overlay's position on scroll. - * @param config Configuration to be used inside the scroll strategy. - * Allows debouncing the reposition calls. - */ - reposition = (config?: IRepositionScrollStrategyConfig) => new RepositionScrollStrategy( - this._scrollDispatcher, this._viewportRuler, this._ngZone, config) -} diff --git a/packages/cdk/overlay/scroll/scroll-strategy.ts b/packages/cdk/overlay/scroll/scroll-strategy.ts deleted file mode 100644 index a240f65b7..000000000 --- a/packages/cdk/overlay/scroll/scroll-strategy.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { IOverlayReference } from '../overlay-reference'; - - -/** - * Describes a strategy that will be used by an overlay to handle scroll events while it is open. - */ -export interface IScrollStrategy { - /** Enable this scroll strategy (called when the attached overlay is attached to a portal). */ - enable(): void; - - /** Disable this scroll strategy (called when the attached overlay is detached from a portal). */ - disable(): void; - - /** Attaches this `ScrollStrategy` to an overlay. */ - attach(overlayRef: IOverlayReference): void; -} - -/** - * Returns an error to be thrown when attempting to attach an already-attached scroll strategy. - */ -export function getMatScrollStrategyAlreadyAttachedError(): Error { - return Error(`Scroll strategy has already been attached.`); -} diff --git a/packages/cdk/overlay/tsconfig.build.json b/packages/cdk/overlay/tsconfig.build.json deleted file mode 100644 index 12134c9ef..000000000 --- a/packages/cdk/overlay/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/overlay", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/platform/features/input-types.ts b/packages/cdk/platform/features/input-types.ts deleted file mode 100644 index 0663cac85..000000000 --- a/packages/cdk/platform/features/input-types.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** Cached result Set of input types support by the current browser. */ -let supportedInputTypes: Set; - -/** Types of `` that *might* be supported. */ -const candidateInputTypes = [ - // `color` must come first. Chrome 56 shows a warning if we change the type to `color` after - // first changing it to something else: - // The specified value "" does not conform to the required format. - // The format is "#rrggbb" where rr, gg, bb are two-digit hexadecimal numbers. - 'color', - 'button', - 'checkbox', - 'date', - 'datetime-local', - 'email', - 'file', - 'hidden', - 'image', - 'month', - 'number', - 'password', - 'radio', - 'range', - 'reset', - 'search', - 'submit', - 'tel', - 'text', - 'time', - 'url', - 'week' -]; - -/** @returns The input types supported by this browser. */ -export function getSupportedInputTypes(): Set { - // Result is cached. - if (supportedInputTypes) { - return supportedInputTypes; - } - - // We can't check if an input type is not supported until we're on the browser, so say that - // everything is supported when not on the browser. We don't use `Platform` here since it's - // just a helper function and can't inject it. - if (typeof document !== 'object' || !document) { - supportedInputTypes = new Set(candidateInputTypes); - - return supportedInputTypes; - } - - const featureTestInput = document.createElement('input'); - - supportedInputTypes = new Set(candidateInputTypes.filter((value) => { - featureTestInput.setAttribute('type', value); - - return featureTestInput.type === value; - })); - - return supportedInputTypes; -} diff --git a/packages/cdk/platform/features/passive-listeners.ts b/packages/cdk/platform/features/passive-listeners.ts deleted file mode 100644 index ad30b5c21..000000000 --- a/packages/cdk/platform/features/passive-listeners.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** Cached result of whether the user's browser supports passive event listeners. */ -let supportsPassiveEvents: boolean; - -/** - * Checks whether the user's browser supports passive event listeners. - * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md - */ -export function supportsPassiveEventListeners(): boolean { - if (supportsPassiveEvents == null && typeof window !== 'undefined') { - try { - window.addEventListener('test', null!, Object.defineProperty({}, 'passive', { - get: () => supportsPassiveEvents = true - })); - } finally { - supportsPassiveEvents = supportsPassiveEvents || false; - } - } - - return supportsPassiveEvents; -} - -/** - * Normalizes an `AddEventListener` object to something that can be passed - * to `addEventListener` on any browser, no matter whether it supports the - * `options` parameter. - * @param options Object to be normalized. - */ -export function normalizePassiveListenerOptions(options: AddEventListenerOptions): - AddEventListenerOptions | boolean { - return supportsPassiveEventListeners() ? options : !!options.capture; -} diff --git a/packages/cdk/platform/features/scrolling.ts b/packages/cdk/platform/features/scrolling.ts deleted file mode 100644 index f6da65653..000000000 --- a/packages/cdk/platform/features/scrolling.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** The possible ways the browser may handle the horizontal scroll axis in RTL languages. */ -export enum RtlScrollAxisType { - /** - * scrollLeft is 0 when scrolled all the way left and (scrollWidth - clientWidth) when scrolled - * all the way right. - */ - NORMAL, - /** - * scrollLeft is -(scrollWidth - clientWidth) when scrolled all the way left and 0 when scrolled - * all the way right. - */ - NEGATED, - /** - * scrollLeft is (scrollWidth - clientWidth) when scrolled all the way left and 0 when scrolled - * all the way right. - */ - INVERTED -} - -/** Cached result of the way the browser handles the horizontal scroll axis in RTL mode. */ -let rtlScrollAxisType: RtlScrollAxisType; - -/** Check whether the browser supports scroll behaviors. */ -export function supportsScrollBehavior(): boolean { - return !!(typeof document == 'object' && 'scrollBehavior' in document.documentElement!.style); -} - -/** - * Checks the type of RTL scroll axis used by this browser. As of time of writing, Chrome is NORMAL, - * Firefox & Safari are NEGATED, and IE & Edge are INVERTED. - */ -export function getRtlScrollAxisType(): RtlScrollAxisType { - // We can't check unless we're on the browser. Just assume 'normal' if we're not. - if (typeof document !== 'object' || !document) { - return RtlScrollAxisType.NORMAL; - } - - if (!rtlScrollAxisType) { - // Create a 1px wide scrolling container and a 2px wide content element. - const scrollContainer = document.createElement('div'); - const containerStyle = scrollContainer.style; - scrollContainer.dir = 'rtl'; - containerStyle.height = '1px'; - containerStyle.width = '1px'; - containerStyle.overflow = 'auto'; - containerStyle.visibility = 'hidden'; - containerStyle.pointerEvents = 'none'; - containerStyle.position = 'absolute'; - - const content = document.createElement('div'); - const contentStyle = content.style; - contentStyle.width = '2px'; - contentStyle.height = '1px'; - - scrollContainer.appendChild(content); - document.body.appendChild(scrollContainer); - - rtlScrollAxisType = RtlScrollAxisType.NORMAL; - - // The viewport starts scrolled all the way to the right in RTL mode. If we are in a NORMAL - // browser this would mean that the scrollLeft should be 1. If it's zero instead we know we're - // dealing with one of the other two types of browsers. - if (scrollContainer.scrollLeft === 0) { - // In a NEGATED browser the scrollLeft is always somewhere in [-maxScrollAmount, 0]. For an - // INVERTED browser it is always somewhere in [0, maxScrollAmount]. We can determine which by - // setting to the scrollLeft to 1. This is past the max for a NEGATED browser, so it will - // return 0 when we read it again. - scrollContainer.scrollLeft = 1; - rtlScrollAxisType = - scrollContainer.scrollLeft === 0 ? RtlScrollAxisType.NEGATED : RtlScrollAxisType.INVERTED; - } - - scrollContainer.parentNode!.removeChild(scrollContainer); - } - - return rtlScrollAxisType; -} diff --git a/packages/cdk/platform/index.ts b/packages/cdk/platform/index.ts deleted file mode 100644 index 2530908a6..000000000 --- a/packages/cdk/platform/index.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export * from './public-api'; diff --git a/packages/cdk/platform/platform-module.ts b/packages/cdk/platform/platform-module.ts deleted file mode 100644 index 1e3ea0fa5..000000000 --- a/packages/cdk/platform/platform-module.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NgModule } from '@angular/core'; - - -@NgModule({}) -export class PlatformModule {} diff --git a/packages/cdk/platform/platform.md b/packages/cdk/platform/platform.md deleted file mode 100644 index 0ad007d99..000000000 --- a/packages/cdk/platform/platform.md +++ /dev/null @@ -1,3 +0,0 @@ -### Platform - -A service for determing the current platform. \ No newline at end of file diff --git a/packages/cdk/platform/platform.ts b/packages/cdk/platform/platform.ts deleted file mode 100644 index 3e32c0d08..000000000 --- a/packages/cdk/platform/platform.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { isPlatformBrowser } from '@angular/common'; -import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core'; - - -// Whether the current platform supports the V8 Break Iterator. The V8 check -// is necessary to detect all Blink based browsers. -const hasV8BreakIterator = (typeof Intl !== 'undefined' && (Intl as any).v8BreakIterator); - -/** - * Service to detect the current platform by comparing the userAgent strings and - * checking browser-specific global properties. - */ -@Injectable({ providedIn: 'root' }) -export class Platform { - /** - * Whether the Angular application is being rendered in the browser. - * We want to use the Angular platform check because if the Document is shimmed - * without the navigator, the following checks will fail. This is preferred because - * sometimes the Document may be shimmed without the user's knowledge or intention - */ - isBrowser: boolean = this._platformId ? - isPlatformBrowser(this._platformId) : typeof document === 'object' && !!document; - - /** Whether the current browser is Microsoft Edge. */ - EDGE: boolean = this.isBrowser && /(edge)/i.test(navigator.userAgent); - - /** Whether the current rendering engine is Microsoft Trident. */ - TRIDENT: boolean = this.isBrowser && /(msie|trident)/i.test(navigator.userAgent); - - /** Whether the current rendering engine is Blink. */ - // EdgeHTML and Trident mock Blink specific things and need to be excluded from this check. - BLINK: boolean = this.isBrowser && (!!((window as any).chrome || hasV8BreakIterator) && - typeof CSS !== 'undefined' && !this.EDGE && !this.TRIDENT); - - /** Whether the current rendering engine is WebKit. */ - // Webkit is part of the userAgent in EdgeHTML, Blink and Trident. Therefore we need to - // ensure that Webkit runs standalone and is not used as another engine's base. - WEBKIT: boolean = this.isBrowser && - /AppleWebKit/i.test(navigator.userAgent) && !this.BLINK && !this.EDGE && !this.TRIDENT; - - /** Whether the current platform is Apple iOS. */ - IOS: boolean = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) && - !(window as any).MSStream; - - /** Whether the current browser is Firefox. */ - // It's difficult to detect the plain Gecko engine, because most of the browsers identify - // them self as Gecko-like browsers and modify the userAgent's according to that. - // Since we only cover one explicit Firefox case, we can simply check for Firefox - // instead of having an unstable check for Gecko. - FIREFOX: boolean = this.isBrowser && /(firefox|minefield)/i.test(navigator.userAgent); - - /** Whether the current platform is Android. */ - // Trident on mobile adds the android platform to the userAgent to trick detections. - ANDROID: boolean = this.isBrowser && /android/i.test(navigator.userAgent) && !this.TRIDENT; - - /** Whether the current browser is Safari. */ - // Safari browsers will include the Safari keyword in their userAgent. Some browsers may fake - // this and just place the Safari keyword in the userAgent. To be more safe about Safari every - // Safari browser should also use Webkit as its layout engine. - SAFARI: boolean = this.isBrowser && /safari/i.test(navigator.userAgent) && this.WEBKIT; - - constructor(@Optional() @Inject(PLATFORM_ID) private _platformId?: Object) {} -} diff --git a/packages/cdk/platform/public-api.ts b/packages/cdk/platform/public-api.ts deleted file mode 100644 index a4d2204a3..000000000 --- a/packages/cdk/platform/public-api.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export * from './platform'; -export * from './platform-module'; -export * from './features/input-types'; -export * from './features/passive-listeners'; -export * from './features/scrolling'; diff --git a/packages/cdk/platform/tsconfig.build.json b/packages/cdk/platform/tsconfig.build.json deleted file mode 100644 index 82affc302..000000000 --- a/packages/cdk/platform/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/platform", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/portal/dom-portal-outlet.ts b/packages/cdk/portal/dom-portal-outlet.ts deleted file mode 100644 index 23e82d94f..000000000 --- a/packages/cdk/portal/dom-portal-outlet.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { - ComponentFactoryResolver, - ComponentRef, - EmbeddedViewRef, - ApplicationRef, - Injector -} from '@angular/core'; - -import { BasePortalOutlet, ComponentPortal, TemplatePortal } from './portal'; - - -/** - * A PortalOutlet for attaching portals to an arbitrary DOM element outside of the Angular - * application context. - */ -export class DomPortalOutlet extends BasePortalOutlet { - constructor( - /** Element into which the content is projected. */ - public outletElement: Element, - private _componentFactoryResolver: ComponentFactoryResolver, - private _appRef: ApplicationRef, - private _defaultInjector: Injector) { - super(); - } - - /** - * Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver. - * @param portal Portal to be attached - * @returns Reference to the created component. - */ - attachComponentPortal(portal: ComponentPortal): ComponentRef { - const resolver = portal.componentFactoryResolver || this._componentFactoryResolver; - const componentFactory = resolver.resolveComponentFactory(portal.component); - let componentRef: ComponentRef; - - // If the portal specifies a ViewContainerRef, we will use that as the attachment point - // for the component (in terms of Angular's component tree, not rendering). - // When the ViewContainerRef is missing, we use the factory to create the component directly - // and then manually attach the view to the application. - if (portal.viewContainerRef) { - componentRef = portal.viewContainerRef.createComponent( - componentFactory, - portal.viewContainerRef.length, - portal.injector || portal.viewContainerRef.injector); - - this.setDisposeFn(() => componentRef.destroy()); - } else { - componentRef = componentFactory.create(portal.injector || this._defaultInjector); - this._appRef.attachView(componentRef.hostView); - this.setDisposeFn(() => { - this._appRef.detachView(componentRef.hostView); - componentRef.destroy(); - }); - } - // At this point the component has been instantiated, so we move it to the location in the DOM - // where we want it to be rendered. - this.outletElement.appendChild(this.getComponentRootNode(componentRef)); - - return componentRef; - } - - /** - * Attaches a template portal to the DOM as an embedded view. - * @param portal Portal to be attached. - * @returns Reference to the created embedded view. - */ - attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { - let viewContainer = portal.viewContainerRef; //tslint:disable-line - let viewRef = viewContainer.createEmbeddedView(portal.templateRef, portal.context); //tslint:disable-line - viewRef.detectChanges(); - - // The method `createEmbeddedView` will add the view as a child of the viewContainer. - // But for the DomPortalOutlet the view can be added everywhere in the DOM - // (e.g Overlay Container) To move the view to the specified host element. We just - // re-append the existing root nodes. - viewRef.rootNodes.forEach((rootNode) => this.outletElement.appendChild(rootNode)); - - this.setDisposeFn((() => { - let index = viewContainer.indexOf(viewRef); //tslint:disable-line - if (index !== -1) { - viewContainer.remove(index); - } - })); - - return viewRef; - } - - /** - * Clears out a portal from the DOM. - */ - dispose(): void { - super.dispose(); - if (this.outletElement.parentNode != null) { - this.outletElement.parentNode.removeChild(this.outletElement); - } - } - - /** Gets the root HTMLElement for an instantiated component. */ - private getComponentRootNode(componentRef: ComponentRef): HTMLElement { - return (componentRef.hostView as EmbeddedViewRef).rootNodes[0] as HTMLElement; - } -} diff --git a/packages/cdk/portal/index.ts b/packages/cdk/portal/index.ts deleted file mode 100644 index 7e1a213e3..000000000 --- a/packages/cdk/portal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public-api'; diff --git a/packages/cdk/portal/portal-directives.ts b/packages/cdk/portal/portal-directives.ts deleted file mode 100644 index 9c1c9480f..000000000 --- a/packages/cdk/portal/portal-directives.ts +++ /dev/null @@ -1,161 +0,0 @@ - -import { - ComponentFactoryResolver, - ComponentRef, - Directive, - EmbeddedViewRef, - EventEmitter, - NgModule, - OnDestroy, - OnInit, - Output, - TemplateRef, - ViewContainerRef -} from '@angular/core'; - -import { BasePortalOutlet, ComponentPortal, Portal, TemplatePortal } from './portal'; - - -/** - * Directive version of a `TemplatePortal`. Because the directive *is* a TemplatePortal, - * the directive instance itself can be attached to a host, enabling declarative use of portals. - */ -@Directive({ - selector: '[cdk-portal], [cdkPortal], [portal]', - exportAs: 'cdkPortal' -}) -export class CdkPortal extends TemplatePortal { - constructor(templateRef: TemplateRef, viewContainerRef: ViewContainerRef) { - super(templateRef, viewContainerRef); - } -} - -/** - * Possible attached references to the CdkPortalOutlet. - */ -export type CdkPortalOutletAttachedRef = ComponentRef | EmbeddedViewRef | null; - - -/** - * Directive version of a PortalOutlet. Because the directive *is* a PortalOutlet, portals can be - * directly attached to it, enabling declarative use. - * - * Usage: - * `` - */ -@Directive({ - selector: '[cdkPortalOutlet], [cdkPortalHost], [portalHost]', - exportAs: 'cdkPortalOutlet, cdkPortalHost', - inputs: ['portal: cdkPortalOutlet'] -}) -export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestroy { - - @Output() attached: EventEmitter = - new EventEmitter(); - - /** Whether the portal component is initialized. */ - private _isInitialized = false; - - /** Reference to the currently-attached component/view ref. */ - private _attachedRef: CdkPortalOutletAttachedRef; - - constructor( - private _componentFactoryResolver: ComponentFactoryResolver, - private _viewContainerRef: ViewContainerRef) { - super(); - } - - /** Portal associated with the Portal outlet. */ - get portal(): Portal | null { - return this._attachedPortal; - } - - set portal(portal: Portal | null) { - // Ignore the cases where the `portal` is set to a falsy value before the lifecycle hooks have - // run. This handles the cases where the user might do something like `
` - // and attach a portal programmatically in the parent component. When Angular does the first CD - // round, it will fire the setter with empty string, causing the user's content to be cleared. - if (this.hasAttached() && !portal && !this._isInitialized) { - return; - } - - if (this.hasAttached()) { - super.detach(); - } - - if (portal) { - super.attach(portal); - } - - this._attachedPortal = portal; - } - - /** Component or view reference that is attached to the portal. */ - get attachedRef(): CdkPortalOutletAttachedRef { - return this._attachedRef; - } - - ngOnInit() { - this._isInitialized = true; - } - - ngOnDestroy() { - super.dispose(); - this._attachedPortal = null; - this._attachedRef = null; - } - - /** - * Attach the given ComponentPortal to this PortalOutlet using the ComponentFactoryResolver. - * - * @param portal Portal to be attached to the portal outlet. - * @returns Reference to the created component. - */ - attachComponentPortal(portal: ComponentPortal): ComponentRef { - portal.setAttachedHost(this); - - // If the portal specifies an origin, use that as the logical location of the component - // in the application tree. Otherwise use the location of this PortalOutlet. - const viewContainerRef = portal.viewContainerRef != null ? - portal.viewContainerRef : - this._viewContainerRef; - - const resolver = portal.componentFactoryResolver || this._componentFactoryResolver; - - const componentFactory = resolver.resolveComponentFactory(portal.component); - const ref = viewContainerRef.createComponent( - componentFactory, viewContainerRef.length, - portal.injector || viewContainerRef.injector); - - super.setDisposeFn(() => ref.destroy()); - this._attachedPortal = portal; - this._attachedRef = ref; - this.attached.emit(ref); - - return ref; - } - - /** - * Attach the given TemplatePortal to this PortlHost as an embedded View. - * @param portal Portal to be attached. - * @returns Reference to the created embedded view. - */ - attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { - portal.setAttachedHost(this); - const viewRef = this._viewContainerRef.createEmbeddedView(portal.templateRef, portal.context); - super.setDisposeFn(() => this._viewContainerRef.clear()); - - this._attachedPortal = portal; - this._attachedRef = viewRef; - this.attached.emit(viewRef); - - return viewRef; - } -} - - -@NgModule({ - exports: [CdkPortal, CdkPortalOutlet], - declarations: [CdkPortal, CdkPortalOutlet] -}) -export class PortalModule {} diff --git a/packages/cdk/portal/portal-errors.ts b/packages/cdk/portal/portal-errors.ts deleted file mode 100644 index 4fa35e84b..000000000 --- a/packages/cdk/portal/portal-errors.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Throws an exception when attempting to attach a null portal to a host. - * @docs-private - */ -export function throwNullPortalError() { - throw Error('Must provide a portal to attach'); -} - -/** - * Throws an exception when attempting to attach a portal to a host that is already attached. - * @docs-private - */ -export function throwPortalAlreadyAttachedError() { - throw Error('Host already has a portal attached'); -} - -/** - * Throws an exception when attempting to attach a portal to an already-disposed host. - * @docs-private - */ -export function throwPortalOutletAlreadyDisposedError() { - throw Error('This PortalOutlet has already been disposed'); -} - -/** - * Throws an exception when attempting to attach an unknown portal type. - * @docs-private - */ -export function throwUnknownPortalTypeError() { - throw Error('Attempting to attach an unknown Portal type. BasePortalOutlet accepts either ' + - 'a ComponentPortal or a TemplatePortal.'); -} - -/** - * Throws an exception when attempting to attach a portal to a null host. - * @docs-private - */ -export function throwNullPortalOutletError() { - throw Error('Attempting to attach a portal to a null PortalOutlet'); -} - -/** - * Throws an exception when attempting to detach a portal that is not attached. - * @docs-private - */ -export function throwNoPortalAttachedError() { - throw Error('Attempting to detach a portal that is not attached to a host'); -} diff --git a/packages/cdk/portal/portal-injector.ts b/packages/cdk/portal/portal-injector.ts deleted file mode 100644 index 55afeab60..000000000 --- a/packages/cdk/portal/portal-injector.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Injector } from '@angular/core'; - - -/** - * Custom injector to be used when providing custom - * injection tokens to components inside a portal. - * @docs-private - */ -export class PortalInjector implements Injector { - constructor( - private _parentInjector: Injector, - private _customTokens: WeakMap) { - } - - get(token: any, notFoundValue?: any): any { // tslint:disable-line - const value = this._customTokens.get(token); - - if (value !== undefined) { - return value; - } - - return this._parentInjector.get(token, notFoundValue); - } -} diff --git a/packages/cdk/portal/portal.spec.ts b/packages/cdk/portal/portal.spec.ts deleted file mode 100644 index 80b6bee8f..000000000 --- a/packages/cdk/portal/portal.spec.ts +++ /dev/null @@ -1,608 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { - NgModule, - Component, - ViewChild, - ViewChildren, - QueryList, - ViewContainerRef, - ComponentFactoryResolver, - Optional, - Injector, - ApplicationRef, - TemplateRef, - ComponentRef -} from '@angular/core'; -import { inject, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DomPortalOutlet } from './dom-portal-outlet'; -import { Portal, ComponentPortal, TemplatePortal } from './portal'; -import { CdkPortal, CdkPortalOutlet, PortalModule } from './portal-directives'; - - -describe('Portals', () => { - - beforeEach(() => { - TestBed - .configureTestingModule({ imports: [PortalModule, PortalTestModule] }) - .compileComponents(); - }); - - describe('CdkPortalOutlet', () => { - let fixture: ComponentFixture; - - beforeEach(() => { - fixture = TestBed.createComponent(PortalTestApp); - }); - - it('should load a component into the portal', () => { - // Set the selectedHost to be a ComponentPortal. - const testAppComponent = fixture.componentInstance; - const componentPortal = new ComponentPortal(PizzaMsg); - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - - testAppComponent.selectedPortal = componentPortal; - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - expect(hostContainer.textContent).toContain('Pizza'); - expect(testAppComponent.portalOutlet.portal).toBe(componentPortal); - expect(testAppComponent.portalOutlet.attachedRef instanceof ComponentRef).toBe(true); - expect(testAppComponent.attachedSpy) - .toHaveBeenCalledWith(testAppComponent.portalOutlet.attachedRef); - }); - - it('should load a template into the portal', () => { - const testAppComponent = fixture.componentInstance; - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - const templatePortal = new TemplatePortal(testAppComponent.templateRef, null!); // tslint:disable-line - - testAppComponent.selectedPortal = templatePortal; - fixture.detectChanges(); - - // Expect that the content of the attached portal is present and no context is projected - expect(hostContainer.textContent).toContain('Banana'); - expect(testAppComponent.portalOutlet.portal).toBe(templatePortal); - - // We can't test whether it's an instance of an `EmbeddedViewRef` so - // we verify that it's defined and that it's not a ComponentRef. - expect(testAppComponent.portalOutlet.attachedRef instanceof ComponentRef).toBe(false); - expect(testAppComponent.portalOutlet.attachedRef).toBeTruthy(); - expect(testAppComponent.attachedSpy) - .toHaveBeenCalledWith(testAppComponent.portalOutlet.attachedRef); - }); - - it('should project template context bindings in the portal', () => { - let testAppComponent = fixture.componentInstance; // tslint:disable-line - let hostContainer = fixture.nativeElement.querySelector('.portal-container'); // tslint:disable-line - - // TemplatePortal without context: - let templatePortal = new TemplatePortal(testAppComponent.templateRef, null!); // tslint:disable-line - testAppComponent.selectedPortal = templatePortal; - fixture.detectChanges(); - // Expect that the content of the attached portal is present and NO context is projected - expect(hostContainer.textContent).toContain('Banana - !'); - - // using TemplatePortal.attach method to set context - testAppComponent.selectedPortal = undefined; - fixture.detectChanges(); - templatePortal.attach(testAppComponent.portalOutlet, { $implicit: { status: 'rotten' } }); - fixture.detectChanges(); - // Expect that the content of the attached portal is present and context given via the - // attach method is projected - expect(hostContainer.textContent).toContain('Banana - rotten!'); - - // using TemplatePortal constructor to set the context - templatePortal = - new TemplatePortal(testAppComponent.templateRef, null!, { $implicit: { status: 'fresh' } }); // tslint:disable-line - testAppComponent.selectedPortal = templatePortal; - fixture.detectChanges(); - // Expect that the content of the attached portal is present and context given via the - // constructor is projected - expect(hostContainer.textContent).toContain('Banana - fresh!'); - - // using TemplatePortal constructor to set the context but also calling attach method with - // context, the latter should take precedence: - testAppComponent.selectedPortal = undefined; - fixture.detectChanges(); - templatePortal.attach(testAppComponent.portalOutlet, { $implicit: { status: 'rotten' } }); - fixture.detectChanges(); - // Expect that the content of the attached portal is present and and context given via the - // attach method is projected and get precedence over constructor context - expect(hostContainer.textContent).toContain('Banana - rotten!'); - }); - - it('should dispose the host when destroyed', () => { - // Set the selectedHost to be a ComponentPortal. - const testAppComponent = fixture.componentInstance; - testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); - - fixture.detectChanges(); - expect(testAppComponent.selectedPortal.isAttached).toBe(true); - - fixture.destroy(); - expect(testAppComponent.selectedPortal.isAttached).toBe(false); - }); - - it('should load a component into the portal with a given injector', () => { - // Create a custom injector for the component. - const chocolateInjector = new ChocolateInjector(fixture.componentInstance.injector); - - // Set the selectedHost to be a ComponentPortal. - const testAppComponent = fixture.componentInstance; - testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg, undefined, chocolateInjector); - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - expect(hostContainer.textContent).toContain('Pizza'); - expect(hostContainer.textContent).toContain('Chocolate'); - }); - - it('should load a portal', () => { - const testAppComponent = fixture.componentInstance; - - // Detect changes initially so that the component's ViewChildren are resolved. - fixture.detectChanges(); - - // Set the selectedHost to be a TemplatePortal. - testAppComponent.selectedPortal = testAppComponent.cakePortal; - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - expect(hostContainer.textContent).toContain('Cake'); - }); - - it('should load a portal with the `*` sugar', () => { - const testAppComponent = fixture.componentInstance; - - // Detect changes initially so that the component's ViewChildren are resolved. - fixture.detectChanges(); - - // Set the selectedHost to be a TemplatePortal (with the `*` syntax). - testAppComponent.selectedPortal = testAppComponent.piePortal; - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - expect(hostContainer.textContent).toContain('Pie'); - }); - - it('should load a portal with a binding', () => { - const testAppComponent = fixture.componentInstance; - - // Detect changes initially so that the component's ViewChildren are resolved. - fixture.detectChanges(); - - // Set the selectedHost to be a TemplatePortal. - testAppComponent.selectedPortal = testAppComponent.portalWithBinding; - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - expect(hostContainer.textContent).toContain('Banana'); - - // When updating the binding value. - testAppComponent.fruit = 'Mango'; - fixture.detectChanges(); - - // Expect the new value to be reflected in the rendered output. - expect(hostContainer.textContent).toContain('Mango'); - }); - - it('should load a portal with an inner template', () => { - const testAppComponent = fixture.componentInstance; - - // Detect changes initially so that the component's ViewChildren are resolved. - fixture.detectChanges(); - - // Set the selectedHost to be a TemplatePortal. - testAppComponent.selectedPortal = testAppComponent.portalWithTemplate; - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - expect(hostContainer.textContent).toContain('Pineapple'); - - // When updating the binding value. - testAppComponent.fruits = ['Mangosteen']; - fixture.detectChanges(); - - // Expect the new value to be reflected in the rendered output. - expect(hostContainer.textContent).toContain('Mangosteen'); - }); - - it('should change the attached portal', () => { - const testAppComponent = fixture.componentInstance; - - // Detect changes initially so that the component's ViewChildren are resolved. - fixture.detectChanges(); - - // Set the selectedHost to be a ComponentPortal. - testAppComponent.selectedPortal = testAppComponent.piePortal; - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - const hostContainer = fixture.nativeElement.querySelector('.portal-container'); - expect(hostContainer.textContent).toContain('Pie'); - - testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); - fixture.detectChanges(); - - expect(hostContainer.textContent).toContain('Pizza'); - }); - - it('should detach the portal when it is set to null', () => { - const testAppComponent = fixture.componentInstance; - testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); - - fixture.detectChanges(); - expect(testAppComponent.portalOutlet.hasAttached()).toBe(true); - expect(testAppComponent.portalOutlet.portal).toBe(testAppComponent.selectedPortal); - - testAppComponent.selectedPortal = null!; // tslint:disable-line - fixture.detectChanges(); - - expect(testAppComponent.portalOutlet.hasAttached()).toBe(false); - expect(testAppComponent.portalOutlet.portal).toBeNull(); - }); - - it('should set the `portal` when attaching a component portal programmatically', () => { - const testAppComponent = fixture.componentInstance; - const portal = new ComponentPortal(PizzaMsg); - - testAppComponent.portalOutlet.attachComponentPortal(portal); - - expect(testAppComponent.portalOutlet.portal).toBe(portal); - }); - - it('should set the `portal` when attaching a template portal programmatically', () => { - const testAppComponent = fixture.componentInstance; - fixture.detectChanges(); - - testAppComponent.portalOutlet.attachTemplatePortal(testAppComponent.cakePortal); - - expect(testAppComponent.portalOutlet.portal).toBe(testAppComponent.cakePortal); - }); - - it('should clear the portal reference on destroy', () => { - const testAppComponent = fixture.componentInstance; - - testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); - fixture.detectChanges(); - - expect(testAppComponent.portalOutlet.portal).toBeTruthy(); - - fixture.destroy(); - - expect(testAppComponent.portalOutlet.portal).toBeNull(); - }); - - it('should not clear programmatically-attached portals on init', () => { - fixture.destroy(); - - const unboundFixture = TestBed.createComponent(UnboundPortalTestApp); - - // Note: calling `detectChanges` here will cause a false positive. - // What we're testing is attaching before the first CD cycle. - unboundFixture.componentInstance.portalOutlet.attach(new ComponentPortal(PizzaMsg)); - unboundFixture.detectChanges(); - - expect(unboundFixture.nativeElement.querySelector('.portal-container').textContent) - .toContain('Pizza'); - }); - - it('should be considered attached when attaching using `attach`', () => { - expect(fixture.componentInstance.portalOutlet.hasAttached()).toBe(false); - fixture.componentInstance.portalOutlet.attach(new ComponentPortal(PizzaMsg)); - expect(fixture.componentInstance.portalOutlet.hasAttached()).toBe(true); - }); - - it('should be considered attached when attaching using `attachComponentPortal`', () => { - expect(fixture.componentInstance.portalOutlet.hasAttached()).toBe(false); - fixture.componentInstance.portalOutlet.attachComponentPortal(new ComponentPortal(PizzaMsg)); - expect(fixture.componentInstance.portalOutlet.hasAttached()).toBe(true); - }); - - it('should be considered attached when attaching using `attachTemplatePortal`', () => { - const instance = fixture.componentInstance; - expect(instance.portalOutlet.hasAttached()).toBe(false); - instance.portalOutlet.attachTemplatePortal(new TemplatePortal(instance.templateRef, null!)); // tslint:disable-line - expect(instance.portalOutlet.hasAttached()).toBe(true); - }); - - }); - - describe('DomPortalOutlet', () => { - let componentFactoryResolver: ComponentFactoryResolver; - let someViewContainerRef: ViewContainerRef; - let someInjector: Injector; - let someFixture: ComponentFixture; - let someDomElement: HTMLElement; - let host: DomPortalOutlet; - let injector: Injector; - let appRef: ApplicationRef; - - let deps = [ComponentFactoryResolver, Injector, ApplicationRef]; // tslint:disable-line - beforeEach(inject(deps, (dcl: ComponentFactoryResolver, i: Injector, ar: ApplicationRef) => { - componentFactoryResolver = dcl; - injector = i; - appRef = ar; - })); - - beforeEach(() => { - someDomElement = document.createElement('div'); - host = new DomPortalOutlet(someDomElement, componentFactoryResolver, appRef, injector); - - someFixture = TestBed.createComponent(ArbitraryViewContainerRefComponent); - someViewContainerRef = someFixture.componentInstance.viewContainerRef; - someInjector = someFixture.componentInstance.injector; - }); - - it('should attach and detach a component portal', () => { - const portal = new ComponentPortal(PizzaMsg, someViewContainerRef); - - const componentInstance: PizzaMsg = portal.attach(host).instance; - - expect(componentInstance instanceof PizzaMsg).toBe(true); - expect(someDomElement.textContent).toContain('Pizza'); - - host.detach(); - - expect(someDomElement.innerHTML).toBe(''); - }); - - it('should attach and detach a component portal with a given injector', () => { - const fixture = TestBed.createComponent(ArbitraryViewContainerRefComponent); - someViewContainerRef = fixture.componentInstance.viewContainerRef; - someInjector = fixture.componentInstance.injector; - - const chocolateInjector = new ChocolateInjector(someInjector); - const portal = new ComponentPortal(PizzaMsg, someViewContainerRef, chocolateInjector); - - const componentInstance: PizzaMsg = portal.attach(host).instance; - fixture.detectChanges(); - - expect(componentInstance instanceof PizzaMsg).toBe(true); - expect(someDomElement.textContent).toContain('Pizza'); - expect(someDomElement.textContent).toContain('Chocolate'); - - host.detach(); - - expect(someDomElement.innerHTML).toBe(''); - }); - - it('should attach and detach a template portal', () => { - const fixture = TestBed.createComponent(PortalTestApp); - fixture.detectChanges(); - - fixture.componentInstance.cakePortal.attach(host); - - expect(someDomElement.textContent).toContain('Cake'); - }); - - it('should render a template portal with an inner template', () => { - const fixture = TestBed.createComponent(PortalTestApp); - fixture.detectChanges(); - - fixture.componentInstance.portalWithTemplate.attach(host); - - expect(someDomElement.textContent).toContain('Durian'); - }); - - it('should attach and detach a template portal with a binding', () => { - const fixture = TestBed.createComponent(PortalTestApp); - - const testAppComponent = fixture.componentInstance; - - // Detect changes initially so that the component's ViewChildren are resolved. - fixture.detectChanges(); - - // Attach the TemplatePortal. - testAppComponent.portalWithBinding.attach(host, { $implicit: { status: 'fresh' } }); - fixture.detectChanges(); - - // Now that the portal is attached, change detection has to happen again in order - // for the bindings to update. - fixture.detectChanges(); - - // Expect that the content of the attached portal is present. - expect(someDomElement.textContent).toContain('Banana - fresh'); - - // When updating the binding value. - testAppComponent.fruit = 'Mango'; - fixture.detectChanges(); - - // Expect the new value to be reflected in the rendered output. - expect(someDomElement.textContent).toContain('Mango'); - - host.detach(); - expect(someDomElement.innerHTML).toBe(''); - }); - - it('should change the attached portal', () => { - const fixture = TestBed.createComponent(ArbitraryViewContainerRefComponent); - someViewContainerRef = fixture.componentInstance.viewContainerRef; - - const appFixture = TestBed.createComponent(PortalTestApp); - appFixture.detectChanges(); - - appFixture.componentInstance.piePortal.attach(host); - - expect(someDomElement.textContent).toContain('Pie'); - - host.detach(); - host.attach(new ComponentPortal(PizzaMsg, someViewContainerRef)); - - expect(someDomElement.textContent).toContain('Pizza'); - }); - - it('should attach and detach a component portal without a ViewContainerRef', () => { - const portal = new ComponentPortal(PizzaMsg); - - const componentInstance: PizzaMsg = portal.attach(host).instance; - - expect(componentInstance instanceof PizzaMsg) - .toBe(true, 'Expected a PizzaMsg component to be created'); - expect(someDomElement.textContent) - .toContain('Pizza', 'Expected the static string "Pizza" in the DomPortalOutlet.'); - - componentInstance.snack = new Chocolate(); - someFixture.detectChanges(); - expect(someDomElement.textContent) - .toContain('Chocolate', 'Expected the bound string "Chocolate" in the DomPortalOutlet'); - - host.detach(); - - expect(someDomElement.innerHTML) - .toBe('', 'Expected the DomPortalOutlet to be empty after detach'); - }); - - it('should call the dispose function even if the host has no attached content', () => { - const spy = jasmine.createSpy('host dispose spy'); - - expect(host.hasAttached()).toBe(false, 'Expected host not to have attached content.'); - - host.setDisposeFn(spy); - host.dispose(); - - expect(spy).toHaveBeenCalled(); - }); - - it('should use the `ComponentFactoryResolver` from the portal, if available', () => { - const spy = jasmine.createSpy('resolveComponentFactorySpy'); - const portal = new ComponentPortal(PizzaMsg, undefined, undefined, { - resolveComponentFactory: (...args: any[]) => { - spy(); - - return componentFactoryResolver.resolveComponentFactory - .apply(componentFactoryResolver, args); - } - }); - - host.attachComponentPortal(portal); - expect(spy).toHaveBeenCalled(); - }); - }); -}); - - -class Chocolate { - toString() { - return 'Chocolate'; - } -} - -class ChocolateInjector { - constructor(public parentInjector: Injector) { - } - - get(token: any) { // tslint:disable-line - return token === Chocolate ? new Chocolate() : this.parentInjector.get(token); // tslint:disable-line - } -} - -/** Simple component for testing ComponentPortal. */ -@Component({ - selector: 'pizza-msg', - template: '

Pizza

{{snack}}

' -}) -class PizzaMsg { - constructor(@Optional() public snack: Chocolate) { - } -} - -/** Simple component to grab an arbitrary ViewContainerRef */ -@Component({ - selector: 'some-placeholder', - template: '

Hello

' -}) -class ArbitraryViewContainerRefComponent { - constructor(public viewContainerRef: ViewContainerRef, public injector: Injector) { - } -} - - -/** Test-bed component that contains a portal outlet and a couple of template portals. */ -@Component({ - selector: 'portal-test', - template: ` -
- -
- - Cake - -
Pie
- {{fruit}} - {{ data?.status }} - - -
    -
  • {{fruitName}}
  • -
-
- - {{fruit}} - {{ data?.status }}! - ` -}) -class PortalTestApp { - @ViewChildren(CdkPortal) portals: QueryList; - @ViewChild(CdkPortalOutlet, {static: true}) portalOutlet: CdkPortalOutlet; - @ViewChild('templateRef', { read: TemplateRef, static: true }) templateRef: TemplateRef; - - selectedPortal: Portal | undefined; - fruit: string = 'Banana'; - fruits = ['Apple', 'Pineapple', 'Durian']; - attachedSpy = jasmine.createSpy('attached spy'); - - constructor(public injector: Injector) { - } - - get cakePortal() { - return this.portals.first; - } - - get piePortal() { - return this.portals.toArray()[1]; - } - - get portalWithBinding() { - return this.portals.toArray()[2]; // tslint:disable-line - } - - get portalWithTemplate() { - return this.portals.toArray()[3]; // tslint:disable-line - } - -} - -/** Test-bed component that contains a portal outlet and a couple of template portals. */ -@Component({ - template: ` -
- -
- ` -}) -class UnboundPortalTestApp { - @ViewChild(CdkPortalOutlet, {static: true}) portalOutlet: CdkPortalOutlet; -} - -// Create a real (non-test) NgModule as a workaround for -// https://github.com/angular/angular/issues/10760 -const TEST_COMPONENTS = [ - PortalTestApp, - UnboundPortalTestApp, - ArbitraryViewContainerRefComponent, - PizzaMsg -]; - -@NgModule({ - imports: [CommonModule, PortalModule], - exports: TEST_COMPONENTS, - declarations: TEST_COMPONENTS, - entryComponents: TEST_COMPONENTS -}) -class PortalTestModule { -} diff --git a/packages/cdk/portal/portal.ts b/packages/cdk/portal/portal.ts deleted file mode 100644 index fc4cbd0c3..000000000 --- a/packages/cdk/portal/portal.ts +++ /dev/null @@ -1,255 +0,0 @@ - -import { - TemplateRef, - ViewContainerRef, - ElementRef, - ComponentRef, - EmbeddedViewRef, - Injector, ComponentFactoryResolver -} from '@angular/core'; - -import { - throwNullPortalOutletError, - throwPortalAlreadyAttachedError, - throwNoPortalAttachedError, - throwNullPortalError, - throwPortalOutletAlreadyDisposedError, - throwUnknownPortalTypeError -} from './portal-errors'; - - -/** Interface that can be used to generically type a class. */ -export interface IComponentType { - new (...args: any[]): T; -} - -/** A `PortalOutlet` is an space that can contain a single `Portal`. */ -export interface IPortalOutlet { - /** Attaches a portal to this outlet. */ - attach(portal: Portal): any; - - /** Detaches the currently attached portal from this outlet. */ - detach(): any; - - /** Performs cleanup before the outlet is destroyed. */ - dispose(): void; - - /** Whether there is currently a portal attached to this outlet. */ - hasAttached(): boolean; -} - -/** - * A `Portal` is something that you want to render somewhere else. - * It can be attach to / detached from a `PortalOutlet`. - */ -export abstract class Portal { - private _attachedHost: IPortalOutlet | null; - - /** Attach this portal to a host. */ - attach(host: IPortalOutlet): T { - if (host == null) { - throwNullPortalOutletError(); - } - - if (host.hasAttached()) { - throwPortalAlreadyAttachedError(); - } - - this._attachedHost = host; - - return host.attach(this); - } - - /** Detach this portal from its host */ - detach(): void { - let host = this._attachedHost; //tslint:disable-line - - if (host == null) { - throwNoPortalAttachedError(); - } else { - this._attachedHost = null; - host.detach(); - } - } - - /** Whether this portal is attached to a host. */ - get isAttached(): boolean { - return this._attachedHost != null; - } - - /** - * Sets the PortalOutlet reference without performing `attach()`. This is used directly by - * the PortalOutlet when it is performing an `attach()` or `detach()`. - */ - setAttachedHost(host: IPortalOutlet | null) { - this._attachedHost = host; - } -} - - -/** - * A `ComponentPortal` is a portal that instantiates some Component upon attachment. - */ -export class ComponentPortal extends Portal> { - /** The type of the component that will be instantiated for attachment. */ - component: IComponentType; - - /** - * [Optional] Where the attached component should live in Angular's *logical* component tree. - * This is different from where the component *renders*, which is determined by the PortalOutlet. - * The origin is necessary when the host is outside of the Angular application context. - */ - viewContainerRef?: ViewContainerRef | null; - - /** [Optional] Injector used for the instantiation of the component. */ - injector?: Injector | null; - - /** - * Alternate `ComponentFactoryResolver` to use when resolving the associated component. - * Defaults to using the resolver from the outlet that the portal is attached to. - */ - componentFactoryResolver?: ComponentFactoryResolver | null; - - constructor( - component: IComponentType, - viewContainerRef?: ViewContainerRef | null, - injector?: Injector | null, - componentFactoryResolver?: ComponentFactoryResolver | null) { - super(); - this.component = component; - this.viewContainerRef = viewContainerRef; - this.injector = injector; - this.componentFactoryResolver = componentFactoryResolver; - } -} - -/** - * A `TemplatePortal` is a portal that represents some embedded template (TemplateRef). - */ -export class TemplatePortal extends Portal { - /** The embedded template that will be used to instantiate an embedded View in the host. */ - templateRef: TemplateRef; - - /** Reference to the ViewContainer into which the template will be stamped out. */ - viewContainerRef: ViewContainerRef; - - /** Contextual data to be passed in to the embedded view. */ - context: C | undefined; - - constructor(template: TemplateRef, viewContainerRef: ViewContainerRef, context?: C) { - super(); - this.templateRef = template; - this.viewContainerRef = viewContainerRef; - this.context = context; - } - - get origin(): ElementRef { - return this.templateRef.elementRef; - } - - /** - * Attach the the portal to the provided `PortalOutlet`. - * When a context is provided it will override the `context` property of the `TemplatePortal` - * instance. - */ - attach(host: IPortalOutlet, context: C | undefined = this.context): C { - this.context = context; - - return super.attach(host); - } - - detach(): void { - this.context = undefined; - - return super.detach(); //tslint:disable-line - } -} - -/** - * Partial implementation of PortalOutlet that handles attaching - * ComponentPortal and TemplatePortal. - */ -export abstract class BasePortalOutlet implements IPortalOutlet { - /** The portal currently attached to the host. */ - protected _attachedPortal: Portal | null; - - /** A function that will permanently dispose this host. */ - private _disposeFn: (() => void) | null; - - /** Whether this host has already been permanently disposed. */ - private _isDisposed: boolean = false; - - /** Whether this host has an attached portal. */ - hasAttached(): boolean { - return !!this._attachedPortal; - } - - attach(portal: ComponentPortal): ComponentRef; - - attach(portal: TemplatePortal): EmbeddedViewRef; - - attach(portal: any): any; - - /** Attaches a portal. */ - attach(portal: Portal): any { - if (!portal) { - throwNullPortalError(); - } - - if (this.hasAttached()) { - throwPortalAlreadyAttachedError(); - } - - if (this._isDisposed) { - throwPortalOutletAlreadyDisposedError(); - } - - if (portal instanceof ComponentPortal) { - this._attachedPortal = portal; - - return this.attachComponentPortal(portal); - } else if (portal instanceof TemplatePortal) { - this._attachedPortal = portal; - - return this.attachTemplatePortal(portal); - } - - throwUnknownPortalTypeError(); - } - - abstract attachComponentPortal(portal: ComponentPortal): ComponentRef; - - abstract attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef; - - /** Detaches a previously attached portal. */ - detach(): void { - if (this._attachedPortal) { - this._attachedPortal.setAttachedHost(null); - this._attachedPortal = null; - } - - this._invokeDisposeFn(); - } - - /** Permanently dispose of this portal host. */ - dispose(): void { - if (this.hasAttached()) { - this.detach(); - } - - this._invokeDisposeFn(); - this._isDisposed = true; - } - - /** @docs-private */ - setDisposeFn(fn: () => void) { - this._disposeFn = fn; - } - - private _invokeDisposeFn() { - if (this._disposeFn) { - this._disposeFn(); - this._disposeFn = null; - } - } -} diff --git a/packages/cdk/portal/public-api.ts b/packages/cdk/portal/public-api.ts deleted file mode 100644 index c31d3c4ce..000000000 --- a/packages/cdk/portal/public-api.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from './portal'; -export * from './dom-portal-outlet'; -export * from './portal-directives'; -export * from './portal-injector'; - -export { DomPortalOutlet as DomPortalHost } from './dom-portal-outlet'; -export { - CdkPortalOutlet as PortalHostDirective, - CdkPortal as TemplatePortalDirective -} from './portal-directives'; -export { IPortalOutlet as PortalHost, BasePortalOutlet as BasePortalHost } from './portal'; diff --git a/packages/cdk/portal/tsconfig.build.json b/packages/cdk/portal/tsconfig.build.json deleted file mode 100644 index 76fa93f45..000000000 --- a/packages/cdk/portal/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/portal", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/scrolling/README.md b/packages/cdk/scrolling/README.md deleted file mode 100644 index fb721769f..000000000 --- a/packages/cdk/scrolling/README.md +++ /dev/null @@ -1,14 +0,0 @@ -The `scrolling` package provides helpers for directives that react to scroll events. - -### cdkScrollable and ScrollDispatcher -The `cdkScrollable` directive and the `ScrollDispatcher` service together allow components to -react to scrolling in any of its ancestor scrolling containers. - -The `cdkScrollable` directive should be applied to any element that acts as a scrolling container. -This marks the element as a `Scrollable` and registers it with the `ScrollDispatcher`. The -dispatcher, then, allows components to share both event listeners and knowledge of all of the -scrollable containers in the application. - -### ViewportRuler -The `ViewportRuler` is a service that can be injected and used to measure the bounds of the browser -viewport. \ No newline at end of file diff --git a/packages/cdk/scrolling/fixed-size-virtual-scroll.ts b/packages/cdk/scrolling/fixed-size-virtual-scroll.ts deleted file mode 100644 index 8476b3907..000000000 --- a/packages/cdk/scrolling/fixed-size-virtual-scroll.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { Directive, forwardRef, Input, OnChanges } from '@angular/core'; -import { coerceNumberProperty } from '@ptsecurity/cdk/coercion'; -import { Observable, Subject } from 'rxjs'; -import { distinctUntilChanged } from 'rxjs/operators'; - -import { VIRTUAL_SCROLL_STRATEGY, VirtualScrollStrategy } from './virtual-scroll-strategy'; -import { CdkVirtualScrollViewport } from './virtual-scroll-viewport'; - - -/** Virtual scrolling strategy for lists with items of known fixed size. */ -export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy { - - private _scrolledIndexChange = new Subject(); - /** @docs-private Implemented as part of VirtualScrollStrategy. */ - scrolledIndexChange: Observable = this._scrolledIndexChange.pipe(distinctUntilChanged()); - - /** The attached viewport. */ - private _viewport: CdkVirtualScrollViewport | null = null; - - /** The size of the items in the virtually scrolling list. */ - private _itemSize: number; - - /** The minimum amount of buffer rendered beyond the viewport (in pixels). */ - private _minBufferPx: number; - - /** The number of buffer items to render beyond the edge of the viewport (in pixels). */ - private _maxBufferPx: number; - - /** - * @param itemSize The size of the items in the virtually scrolling list. - * @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more - * @param maxBufferPx The amount of buffer (in pixels) to render when rendering more. - */ - constructor(itemSize: number, minBufferPx: number, maxBufferPx: number) { - this._itemSize = itemSize; - this._minBufferPx = minBufferPx; - this._maxBufferPx = maxBufferPx; - } - - /** - * Attaches this scroll strategy to a viewport. - * @param viewport The viewport to attach this strategy to. - */ - attach(viewport: CdkVirtualScrollViewport) { - this._viewport = viewport; - this._updateTotalContentSize(); - this._updateRenderedRange(); - } - - /** Detaches this scroll strategy from the currently attached viewport. */ - detach() { - this._scrolledIndexChange.complete(); - this._viewport = null; - } - - /** - * Update the item size and buffer size. - * @param itemSize The size of the items in the virtually scrolling list. - * @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more - * @param maxBufferPx The amount of buffer (in pixels) to render when rendering more. - */ - updateItemAndBufferSize(itemSize: number, minBufferPx: number, maxBufferPx: number) { - if (maxBufferPx < minBufferPx) { - throw Error('CDK virtual scroll: maxBufferPx must be greater than or equal to minBufferPx'); - } - this._itemSize = itemSize; - this._minBufferPx = minBufferPx; - this._maxBufferPx = maxBufferPx; - this._updateTotalContentSize(); - this._updateRenderedRange(); - } - - /** @docs-private Implemented as part of VirtualScrollStrategy. */ - onContentScrolled() { - this._updateRenderedRange(); - } - - /** @docs-private Implemented as part of VirtualScrollStrategy. */ - onDataLengthChanged() { - this._updateTotalContentSize(); - this._updateRenderedRange(); - } - - /** @docs-private Implemented as part of VirtualScrollStrategy. */ - onContentRendered() { /* no-op */ - } - - /** @docs-private Implemented as part of VirtualScrollStrategy. */ - onRenderedOffsetChanged() { /* no-op */ - } - - /** - * Scroll to the offset for the given index. - * @param index The index of the element to scroll to. - * @param behavior The ScrollBehavior to use when scrolling. - */ - scrollToIndex(index: number, behavior: ScrollBehavior): void { - if (this._viewport) { - this._viewport.scrollToOffset(index * this._itemSize, behavior); - } - } - - /** Update the viewport's total content size. */ - private _updateTotalContentSize() { - if (!this._viewport) { - return; - } - - this._viewport.setTotalContentSize(this._viewport.getDataLength() * this._itemSize); - } - - /** Update the viewport's rendered range. */ - private _updateRenderedRange() { - if (!this._viewport) { - return; - } - - const scrollOffset = this._viewport.measureScrollOffset(); - const firstVisibleIndex = scrollOffset / this._itemSize; - const renderedRange = this._viewport.getRenderedRange(); - const newRange = {start: renderedRange.start, end: renderedRange.end}; - const viewportSize = this._viewport.getViewportSize(); - const dataLength = this._viewport.getDataLength(); - - const startBuffer = scrollOffset - newRange.start * this._itemSize; - if (startBuffer < this._minBufferPx && newRange.start != 0) { - const expandStart = Math.ceil((this._maxBufferPx - startBuffer) / this._itemSize); - newRange.start = Math.max(0, newRange.start - expandStart); - newRange.end = Math.min(dataLength, - Math.ceil(firstVisibleIndex + (viewportSize + this._minBufferPx) / this._itemSize)); - } else { - const endBuffer = newRange.end * this._itemSize - (scrollOffset + viewportSize); - if (endBuffer < this._minBufferPx && newRange.end != dataLength) { - const expandEnd = Math.ceil((this._maxBufferPx - endBuffer) / this._itemSize); - if (expandEnd > 0) { - newRange.end = Math.min(dataLength, newRange.end + expandEnd); - newRange.start = Math.max(0, - Math.floor(firstVisibleIndex - this._minBufferPx / this._itemSize)); - } - } - } - - this._viewport.setRenderedRange(newRange); - this._viewport.setRenderedContentOffset(this._itemSize * newRange.start); - this._scrolledIndexChange.next(Math.floor(firstVisibleIndex)); - } -} - - -/** - * Provider factory for `FixedSizeVirtualScrollStrategy` that simply extracts the already created - * `FixedSizeVirtualScrollStrategy` from the given directive. - * @param fixedSizeDir The instance of `CdkFixedSizeVirtualScroll` to extract the - * `FixedSizeVirtualScrollStrategy` from. - */ -export function _fixedSizeVirtualScrollStrategyFactory(fixedSizeDir: CdkFixedSizeVirtualScroll) { - return fixedSizeDir._scrollStrategy; -} - - -/** A virtual scroll strategy that supports fixed-size items. */ -@Directive({ - selector: 'cdk-virtual-scroll-viewport[itemSize]', - providers: [{ - provide: VIRTUAL_SCROLL_STRATEGY, - useFactory: _fixedSizeVirtualScrollStrategyFactory, - deps: [forwardRef(() => CdkFixedSizeVirtualScroll)] - }] -}) -export class CdkFixedSizeVirtualScroll implements OnChanges { - /** The size of the items in the list (in pixels). */ - @Input() - get itemSize(): number { - return this._itemSize; - } - - set itemSize(value: number) { - this._itemSize = coerceNumberProperty(value); - } - - _itemSize = 20; - - /** - * The minimum amount of buffer rendered beyond the viewport (in pixels). - * If the amount of buffer dips below this number, more items will be rendered. Defaults to 100px. - */ - @Input() - get minBufferPx(): number { - return this._minBufferPx; - } - - set minBufferPx(value: number) { - this._minBufferPx = coerceNumberProperty(value); - } - - _minBufferPx = 100; - - /** - * The number of pixels worth of buffer to render for when rendering new items. Defaults to 200px. - */ - @Input() - get maxBufferPx(): number { - return this._maxBufferPx; - } - - set maxBufferPx(value: number) { - this._maxBufferPx = coerceNumberProperty(value); - } - - _maxBufferPx = 200; - - /** The scroll strategy used by this directive. */ - _scrollStrategy = - new FixedSizeVirtualScrollStrategy(this.itemSize, this.minBufferPx, this.maxBufferPx); - - ngOnChanges() { - this._scrollStrategy.updateItemAndBufferSize(this.itemSize, this.minBufferPx, this.maxBufferPx); - } -} diff --git a/packages/cdk/scrolling/index.ts b/packages/cdk/scrolling/index.ts deleted file mode 100644 index 2530908a6..000000000 --- a/packages/cdk/scrolling/index.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export * from './public-api'; diff --git a/packages/cdk/scrolling/public-api.ts b/packages/cdk/scrolling/public-api.ts deleted file mode 100644 index d2d7481ad..000000000 --- a/packages/cdk/scrolling/public-api.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export * from './fixed-size-virtual-scroll'; -export * from './scroll-dispatcher'; -export * from './scrollable'; -export * from './scrolling-module'; -export * from './viewport-ruler'; -export * from './virtual-for-of'; -export * from './virtual-scroll-strategy'; -export * from './virtual-scroll-viewport'; diff --git a/packages/cdk/scrolling/scroll-dispatcher.spec.ts b/packages/cdk/scrolling/scroll-dispatcher.spec.ts deleted file mode 100644 index 46386f28e..000000000 --- a/packages/cdk/scrolling/scroll-dispatcher.spec.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { NgModule, Component, ViewChild, ElementRef } from '@angular/core'; -import { inject, TestBed, async, fakeAsync, ComponentFixture, tick } from '@angular/core/testing'; -import { dispatchFakeEvent } from '@ptsecurity/cdk/testing'; - -import { CdkScrollable, ScrollDispatcher, ScrollingModule } from './public-api'; - - -describe('ScrollDispatcher', () => { - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ScrollTestModule] - }); - - TestBed.compileComponents(); - })); - - describe('Basic usage', () => { - let scroll: ScrollDispatcher; - let fixture: ComponentFixture; - - beforeEach(inject([ScrollDispatcher], (s: ScrollDispatcher) => { - scroll = s; - - fixture = TestBed.createComponent(ScrollingComponent); - fixture.detectChanges(); - })); - - it('should be registered with the scrollable directive with the scroll service', () => { - const componentScrollable = fixture.componentInstance.scrollable; - expect(scroll.scrollContainers.has(componentScrollable)).toBe(true); - }); - - it('should have the scrollable directive deregistered when the component is destroyed', () => { - const componentScrollable = fixture.componentInstance.scrollable; - expect(scroll.scrollContainers.has(componentScrollable)).toBe(true); - - fixture.destroy(); - expect(scroll.scrollContainers.has(componentScrollable)).toBe(false); - }); - - it('should notify through the directive and service that a scroll event occurred', - fakeAsync(() => { - // Listen for notifications from scroll directive - const scrollable = fixture.componentInstance.scrollable; - const directiveSpy = jasmine.createSpy('directive scroll callback'); - scrollable.elementScrolled().subscribe(directiveSpy); - - // Listen for notifications from scroll service with a throttle of 100ms - const throttleTime = 100; - const serviceSpy = jasmine.createSpy('service scroll callback'); - scroll.scrolled(throttleTime).subscribe(serviceSpy); - - // Emit a scroll event from the scrolling element in our component. - // This event should be picked up by the scrollable directive and notify. - // The notification should be picked up by the service. - dispatchFakeEvent(fixture.componentInstance.scrollingElement.nativeElement, 'scroll', false); - - // The scrollable directive should have notified the service immediately. - expect(directiveSpy).toHaveBeenCalled(); - - // Verify that the throttle is used, the service should wait for the throttle time until - // sending the notification. - expect(serviceSpy).not.toHaveBeenCalled(); - - // After the throttle time, the notification should be sent. - tick(throttleTime); - expect(serviceSpy).toHaveBeenCalled(); - })); - - it('should not execute the global events in the Angular zone', () => { - scroll.scrolled(0).subscribe(() => {}); - dispatchFakeEvent(document, 'scroll', false); - - expect(fixture.ngZone!.isStable).toBe(true); - }); - - it('should not execute the scrollable events in the Angular zone', () => { - dispatchFakeEvent(fixture.componentInstance.scrollingElement.nativeElement, 'scroll'); - expect(fixture.ngZone!.isStable).toBe(true); - }); - - it('should be able to unsubscribe from the global scrollable', () => { - const spy = jasmine.createSpy('global scroll callback'); - const subscription = scroll.scrolled(0).subscribe(spy); - - dispatchFakeEvent(document, 'scroll', false); - expect(spy).toHaveBeenCalledTimes(1); - - subscription.unsubscribe(); - dispatchFakeEvent(document, 'scroll', false); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('should complete the `scrolled` stream on destroy', () => { - const completeSpy = jasmine.createSpy('complete spy'); - const subscription = scroll.scrolled(0).subscribe({complete: completeSpy}); - - scroll.ngOnDestroy(); - - expect(completeSpy).toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - - it('should complete the scrollable stream when it is destroyed', () => { - const scrollable = fixture.componentInstance.scrollable; - const spy = jasmine.createSpy('complete spy'); - const subscription = scrollable.elementScrolled().subscribe({complete: spy}); - - fixture.destroy(); - expect(spy).toHaveBeenCalled(); - subscription.unsubscribe(); - }); - - }); - - describe('Nested scrollables', () => { - let scroll: ScrollDispatcher; - let fixture: ComponentFixture; - let element: ElementRef; - - beforeEach(inject([ScrollDispatcher], (s: ScrollDispatcher) => { - scroll = s; - - fixture = TestBed.createComponent(NestedScrollingComponent); - fixture.detectChanges(); - element = fixture.componentInstance.interestingElement; - })); - - it('should be able to identify the containing scrollables of an element', () => { - const scrollContainers = scroll.getAncestorScrollContainers(element); - const scrollableElementIds = - scrollContainers.map((scrollable) => scrollable.getElementRef().nativeElement.id); - - expect(scrollableElementIds).toEqual(['scrollable-1', 'scrollable-1a']); - }); - - it('should emit when one of the ancestor scrollable containers is scrolled', () => { - const spy = jasmine.createSpy('scroll spy'); - const subscription = scroll.ancestorScrolled(element, 0).subscribe(spy); - const grandparent = fixture.debugElement.nativeElement.querySelector('#scrollable-1'); - - dispatchFakeEvent(grandparent, 'scroll', false); - expect(spy).toHaveBeenCalledTimes(1); - - dispatchFakeEvent(window.document, 'scroll', false); - expect(spy).toHaveBeenCalledTimes(2); - - subscription.unsubscribe(); - }); - - it('should not emit when a non-ancestor is scrolled', () => { - const spy = jasmine.createSpy('scroll spy'); - const subscription = scroll.ancestorScrolled(element, 0).subscribe(spy); - const stranger = fixture.debugElement.nativeElement.querySelector('#scrollable-2'); - - dispatchFakeEvent(stranger, 'scroll', false); - expect(spy).not.toHaveBeenCalled(); - - subscription.unsubscribe(); - }); - }); - - describe('lazy subscription', () => { - let scroll: ScrollDispatcher; - - beforeEach(inject([ScrollDispatcher], (s: ScrollDispatcher) => { - scroll = s; - })); - - it('should lazily add global listeners as service subscriptions are added and removed', () => { - expect(scroll._globalSubscription).toBeNull('Expected no global listeners on init.'); - - const subscription = scroll.scrolled(0).subscribe(() => {}); - - expect(scroll._globalSubscription).toBeTruthy( - 'Expected global listeners after a subscription has been added.'); - - subscription.unsubscribe(); - - expect(scroll._globalSubscription).toBeNull( - 'Expected global listeners to have been removed after the subscription has stopped.'); - }); - - it('should remove global listeners on unsubscribe, despite any other live scrollables', () => { - const fixture = TestBed.createComponent(NestedScrollingComponent); - fixture.detectChanges(); - - expect(scroll._globalSubscription).toBeNull('Expected no global listeners on init.'); - expect(scroll.scrollContainers.size).toBe(4, 'Expected multiple scrollables'); - - const subscription = scroll.scrolled(0).subscribe(() => {}); - - expect(scroll._globalSubscription).toBeTruthy( - 'Expected global listeners after a subscription has been added.'); - - subscription.unsubscribe(); - - expect(scroll._globalSubscription).toBeNull( - 'Expected global listeners to have been removed after the subscription has stopped.'); - expect(scroll.scrollContainers.size) - .toBe(4, 'Expected scrollable count to stay the same'); - }); - - it('should remove the global subscription on destroy', () => { - expect(scroll._globalSubscription).toBeNull('Expected no global listeners on init.'); - - const subscription = scroll.scrolled(0).subscribe(() => {}); - - expect(scroll._globalSubscription).toBeTruthy( - 'Expected global listeners after a subscription has been added.'); - - scroll.ngOnDestroy(); - - expect(scroll._globalSubscription).toBeNull( - 'Expected global listeners to have been removed after the subscription has stopped.'); - - subscription.unsubscribe(); - }); - - }); -}); - - -/** Simple component that contains a large div and can be scrolled. */ -@Component({ - template: `
` -}) -class ScrollingComponent { - @ViewChild(CdkScrollable, {static: false}) scrollable: CdkScrollable; - @ViewChild('scrollingElement', {static: false}) scrollingElement: ElementRef; -} - - -/** Component containing nested scrollables. */ -@Component({ - template: ` -
-
-
-
-
-
-
- ` -}) -class NestedScrollingComponent { - @ViewChild('interestingElement', {static: false}) interestingElement: ElementRef; -} - -const TEST_COMPONENTS = [ScrollingComponent, NestedScrollingComponent]; -@NgModule({ - imports: [ScrollingModule], - providers: [ScrollDispatcher], - exports: TEST_COMPONENTS, - declarations: TEST_COMPONENTS, - entryComponents: TEST_COMPONENTS -}) -class ScrollTestModule { } diff --git a/packages/cdk/scrolling/scroll-dispatcher.ts b/packages/cdk/scrolling/scroll-dispatcher.ts deleted file mode 100644 index 4fee3fdb2..000000000 --- a/packages/cdk/scrolling/scroll-dispatcher.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - ElementRef, - Injectable, - NgZone, - OnDestroy -} from '@angular/core'; -import { Platform } from '@ptsecurity/cdk/platform'; -import { fromEvent, of as observableOf, Subject, Subscription, Observable, Observer } from 'rxjs'; -import { auditTime, filter } from 'rxjs/operators'; - -import { CdkScrollable } from './scrollable'; - - -/** Time in ms to throttle the scrolling events by default. */ -export const DEFAULT_SCROLL_TIME = 20; - -/** - * Service contained all registered Scrollable references and emits an event when any one of the - * Scrollable references emit a scrolled event. - */ -@Injectable({providedIn: 'root'}) -export class ScrollDispatcher implements OnDestroy { - - /** - * Map of all the scrollable references that are registered with the service and their - * scroll event subscriptions. - */ - scrollContainers: Map = new Map(); - - /** Keeps track of the global `scroll` and `resize` subscriptions. */ - _globalSubscription: Subscription | null = null; - - /** Subject for notifying that a registered scrollable reference element has been scrolled. */ - private _scrolled = new Subject(); - - /** Keeps track of the amount of subscriptions to `scrolled`. Used for cleaning up afterwards. */ - private scrolledCount = 0; - - constructor(private ngZone: NgZone, private platform: Platform) { } - - /** - * Registers a scrollable instance with the service and listens for its scrolled events. When the - * scrollable is scrolled, the service emits the event to its scrolled observable. - * @param scrollable Scrollable instance to be registered. - */ - register(scrollable: CdkScrollable): void { - if (!this.scrollContainers.has(scrollable)) { - this.scrollContainers.set(scrollable, scrollable.elementScrolled() - .subscribe(() => this._scrolled.next(scrollable))); - } - } - - /** - * Deregisters a Scrollable reference and unsubscribes from its scroll event observable. - * @param scrollable Scrollable instance to be deregistered. - */ - deregister(scrollable: CdkScrollable): void { - const scrollableReference = this.scrollContainers.get(scrollable); - - if (scrollableReference) { - scrollableReference.unsubscribe(); - this.scrollContainers.delete(scrollable); - } - } - - /** - * Returns an observable that emits an event whenever any of the registered Scrollable - * references (or window, document, or body) fire a scrolled event. Can provide a time in ms - * to override the default "throttle" time. - * - * **Note:** in order to avoid hitting change detection for every scroll event, - * all of the events emitted from this stream will be run outside the Angular zone. - * If you need to update any data bindings as a result of a scroll event, you have - * to run the callback using `NgZone.run`. - */ - scrolled(auditTimeInMs: number = DEFAULT_SCROLL_TIME): Observable { - - if (!this.platform.isBrowser) { - return observableOf(); - } - - return new Observable((observer: Observer) => { - if (!this._globalSubscription) { - this.addGlobalListener(); - } - - // In the case of a 0ms delay, use an observable without auditTime - // since it does add a perceptible delay in processing overhead. - const subscription = auditTimeInMs > 0 ? - this._scrolled.pipe(auditTime(auditTimeInMs)).subscribe(observer) : - this._scrolled.subscribe(observer); - - this.scrolledCount++; - - return () => { - subscription.unsubscribe(); - this.scrolledCount--; - - if (!this.scrolledCount) { - this.removeGlobalListener(); - } - }; - }); - } - - ngOnDestroy() { - this.removeGlobalListener(); - this.scrollContainers.forEach((_, container) => this.deregister(container)); - this._scrolled.complete(); - } - - /** - * Returns an observable that emits whenever any of the - * scrollable ancestors of an element are scrolled. - * @param elementRef Element whose ancestors to listen for. - * @param auditTimeInMs Time to throttle the scroll events. - */ - ancestorScrolled(elementRef: ElementRef, auditTimeInMs?: number): Observable { - const ancestors = this.getAncestorScrollContainers(elementRef); - - return this.scrolled(auditTimeInMs).pipe(filter((target) => { - return !target || ancestors.indexOf(target) > -1; - })); - } - - /** Returns all registered Scrollables that contain the provided element. */ - getAncestorScrollContainers(elementRef: ElementRef): CdkScrollable[] { - const scrollingContainers: CdkScrollable[] = []; - - this.scrollContainers.forEach((_subscription: Subscription, scrollable: CdkScrollable) => { - if (this.scrollableContainsElement(scrollable, elementRef)) { - scrollingContainers.push(scrollable); - } - }); - - return scrollingContainers; - } - - /** Returns true if the element is contained within the provided Scrollable. */ - private scrollableContainsElement(scrollable: CdkScrollable, elementRef: ElementRef): boolean { - let element: HTMLElement | null = elementRef.nativeElement; - let scrollableElement = scrollable.getElementRef().nativeElement; - - // Traverse through the element parents until we reach null, checking if any of the elements - // are the scrollable's element. - do { - if (element == scrollableElement) { return true; } - } while (element = element!.parentElement); - - return false; - } - - /** Sets up the global scroll listeners. */ - private addGlobalListener() { - this._globalSubscription = this.ngZone.runOutsideAngular(() => { - return fromEvent(window.document, 'scroll').subscribe(() => this._scrolled.next()); - }); - } - - /** Cleans up the global scroll listener. */ - private removeGlobalListener() { - if (this._globalSubscription) { - this._globalSubscription.unsubscribe(); - this._globalSubscription = null; - } - } -} diff --git a/packages/cdk/scrolling/scrollable.ts b/packages/cdk/scrolling/scrollable.ts deleted file mode 100644 index 21588b03d..000000000 --- a/packages/cdk/scrolling/scrollable.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { Directive, ElementRef, OnInit, OnDestroy, NgZone, Optional } from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { getRtlScrollAxisType, RtlScrollAxisType, supportsScrollBehavior } from '@ptsecurity/cdk/platform'; -import { fromEvent, Observable, Observer, Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; - -import { ScrollDispatcher } from './scroll-dispatcher'; - - -export type _Without = {[P in keyof T]?: never}; -export type _XOR = (_Without & U) | (_Without & T); - -export interface _Top { top?: number; } -export interface _Bottom { bottom?: number; } -export interface _Left { left?: number; } -export interface _Right { right?: number; } -export interface _Start { start?: number; } -export interface _End { end?: number; } - -export type _XAxis = _XOR<_XOR<_Left, _Right>, _XOR<_Start, _End>>; -export type _YAxis = _XOR<_Top, _Bottom>; - -/** - * An extended version of ScrollToOptions that allows expressing scroll offsets relative to the - * top, bottom, left, right, start, or end of the viewport rather than just the top and left. - * Please note: the top and bottom properties are mutually exclusive, as are the left, right, - * start, and end properties. - */ -export type ExtendedScrollToOptions = _XAxis & _YAxis & ScrollOptions; - -/** - * Sends an event when the directive's element is scrolled. Registers itself with the - * ScrollDispatcher service to include itself as part of its collection of scrolling events that it - * can be listened to through the service. - */ -@Directive({ - selector: '[cdk-scrollable], [cdkScrollable]' -}) -export class CdkScrollable implements OnInit, OnDestroy { - - private destroyed = new Subject(); - - private _elementScrolled: Observable = new Observable((observer: Observer) => - this.ngZone.runOutsideAngular(() => - fromEvent(this.elementRef.nativeElement, 'scroll').pipe(takeUntil(this.destroyed)) - .subscribe(observer))); - - constructor(protected elementRef: ElementRef, - protected scrollDispatcher: ScrollDispatcher, - protected ngZone: NgZone, - @Optional() protected dir?: Directionality - ) {} - - ngOnInit() { - this.scrollDispatcher.register(this); - } - - ngOnDestroy() { - this.scrollDispatcher.deregister(this); - this.destroyed.next(); - this.destroyed.complete(); - } - - /** Returns observable that emits when a scroll event is fired on the host element. */ - elementScrolled(): Observable { - return this._elementScrolled; - } - - /** Gets the ElementRef for the viewport. */ - getElementRef(): ElementRef { - return this.elementRef; - } - - /** - * Scrolls to the specified offsets. This is a normalized version of the browser's native scrollTo - * method, since browsers are not consistent about what scrollLeft means in RTL. For this method - * left and right always refer to the left and right side of the scrolling container irrespective - * of the layout direction. start and end refer to left and right in an LTR context and vice-versa - * in an RTL context. - * @param options specified the offsets to scroll to. - */ - scrollTo(options: ExtendedScrollToOptions): void { - const el = this.elementRef.nativeElement; - const isRtl = this.dir && this.dir.value == 'rtl'; - - // Rewrite start & end offsets as right or left offsets. - options.left = options.left == null ? (isRtl ? options.end : options.start) : options.left; - options.right = options.right == null ? (isRtl ? options.start : options.end) : options.right; - - // Rewrite the bottom offset as a top offset. - if (options.bottom != null) { - (options as _Without<_Bottom> & _Top).top = - el.scrollHeight - el.clientHeight - options.bottom; - } - - // Rewrite the right offset as a left offset. - if (isRtl && getRtlScrollAxisType() != RtlScrollAxisType.NORMAL) { - if (options.left != null) { - (options as _Without<_Left> & _Right).right = - el.scrollWidth - el.clientWidth - options.left; - } - - if (getRtlScrollAxisType() == RtlScrollAxisType.INVERTED) { - options.left = options.right; - } else if (getRtlScrollAxisType() == RtlScrollAxisType.NEGATED) { - options.left = options.right ? -options.right : options.right; - } - } else { - if (options.right != null) { - (options as _Without<_Right> & _Left).left = - el.scrollWidth - el.clientWidth - options.right; - } - } - - this.applyScrollToOptions(options); - } - - /** - * Measures the scroll offset relative to the specified edge of the viewport. This method can be - * used instead of directly checking scrollLeft or scrollTop, since browsers are not consistent - * about what scrollLeft means in RTL. The values returned by this method are normalized such that - * left and right always refer to the left and right side of the scrolling container irrespective - * of the layout direction. start and end refer to left and right in an LTR context and vice-versa - * in an RTL context. - * @param from The edge to measure from. - */ - measureScrollOffset(from: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end'): number { - const LEFT = 'left'; - const RIGHT = 'right'; - const el = this.elementRef.nativeElement; - if (from == 'top') { - return el.scrollTop; - } - if (from == 'bottom') { - return el.scrollHeight - el.clientHeight - el.scrollTop; - } - - // Rewrite start & end as left or right offsets. - const isRtl = this.dir && this.dir.value == 'rtl'; - if (from == 'start') { - from = isRtl ? RIGHT : LEFT; - } else if (from == 'end') { - from = isRtl ? LEFT : RIGHT; - } - - if (isRtl && getRtlScrollAxisType() == RtlScrollAxisType.INVERTED) { - // For INVERTED, scrollLeft is (scrollWidth - clientWidth) when scrolled all the way left and - // 0 when scrolled all the way right. - if (from == LEFT) { - return el.scrollWidth - el.clientWidth - el.scrollLeft; - } else { - return el.scrollLeft; - } - } else if (isRtl && getRtlScrollAxisType() == RtlScrollAxisType.NEGATED) { - // For NEGATED, scrollLeft is -(scrollWidth - clientWidth) when scrolled all the way left and - // 0 when scrolled all the way right. - if (from == LEFT) { - return el.scrollLeft + el.scrollWidth - el.clientWidth; - } else { - return -el.scrollLeft; - } - } else { - // For NORMAL, as well as non-RTL contexts, scrollLeft is 0 when scrolled all the way left and - // (scrollWidth - clientWidth) when scrolled all the way right. - if (from == LEFT) { - return el.scrollLeft; - } else { - return el.scrollWidth - el.clientWidth - el.scrollLeft; - } - } - } - - private applyScrollToOptions(options: ScrollToOptions): void { - const el = this.elementRef.nativeElement; - - if (supportsScrollBehavior()) { - el.scrollTo(options); - } else { - if (options.top != null) { - el.scrollTop = options.top; - } - if (options.left != null) { - el.scrollLeft = options.left; - } - } - } - - -} diff --git a/packages/cdk/scrolling/scrolling-module.ts b/packages/cdk/scrolling/scrolling-module.ts deleted file mode 100644 index 2929eac90..000000000 --- a/packages/cdk/scrolling/scrolling-module.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BidiModule } from '@ptsecurity/cdk/bidi'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; - -import { CdkFixedSizeVirtualScroll } from './fixed-size-virtual-scroll'; -import { CdkScrollable } from './scrollable'; -import { CdkVirtualForOf } from './virtual-for-of'; -import { CdkVirtualScrollViewport } from './virtual-scroll-viewport'; - - -@NgModule({ - imports: [BidiModule, PlatformModule], - exports: [ - BidiModule, - CdkFixedSizeVirtualScroll, - CdkScrollable, - CdkVirtualForOf, - CdkVirtualScrollViewport - ], - declarations: [ - CdkFixedSizeVirtualScroll, - CdkScrollable, - CdkVirtualForOf, - CdkVirtualScrollViewport - ] -}) -export class ScrollingModule {} - - -/** - * @deprecated - * @breaking-change - */ -@NgModule({ - imports: [ScrollingModule], - exports: [ScrollingModule] -}) -export class ScrollDispatchModule {} diff --git a/packages/cdk/scrolling/tsconfig.build.json b/packages/cdk/scrolling/tsconfig.build.json deleted file mode 100644 index b99287035..000000000 --- a/packages/cdk/scrolling/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/scrolling", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/scrolling/viewport-ruler.spec.ts b/packages/cdk/scrolling/viewport-ruler.spec.ts deleted file mode 100644 index 64a048aa4..000000000 --- a/packages/cdk/scrolling/viewport-ruler.spec.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { NgZone } from '@angular/core'; -import { TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; -import { dispatchFakeEvent } from '@ptsecurity/cdk/testing'; - -import { ScrollingModule } from './public-api'; -import { ViewportRuler } from './viewport-ruler'; - - -// For all tests, we assume the browser window is 1024x786 (outerWidth x outerHeight). -// The karma config has been set to this for local tests, and it is the default size -// for tests on CI (both SauceLabs and Browserstack). - -// While we know the *outer* window width/height, the innerWidth and innerHeight depend on the -// the size of the individual browser's chrome, so we have to use window.innerWidth and -// window.innerHeight in the unit test instead of hard-coded values. - -describe('ViewportRuler', () => { - let ruler: ViewportRuler; - - const startingWindowWidth = window.innerWidth; - const startingWindowHeight = window.innerHeight; - - // Create a very large element that will make the page scrollable. - const veryLargeElement: HTMLElement = document.createElement('div'); - veryLargeElement.style.width = '6000px'; - veryLargeElement.style.height = '6000px'; - - beforeEach(() => TestBed.configureTestingModule({ - imports: [ScrollingModule], - providers: [ViewportRuler] - })); - - beforeEach(inject([ViewportRuler], (viewportRuler: ViewportRuler) => { - ruler = viewportRuler; - scrollTo(0, 0); - })); - - afterEach(() => { - ruler.ngOnDestroy(); - }); - - it('should get the viewport size', () => { - const size = ruler.getViewportSize(); - expect(size.width).toBe(window.innerWidth); - expect(size.height).toBe(window.innerHeight); - }); - - it('should get the viewport bounds when the page is not scrolled', () => { - const bounds = ruler.getViewportRect(); - expect(bounds.top).toBe(0); - expect(bounds.left).toBe(0); - expect(bounds.bottom).toBe(window.innerHeight); - expect(bounds.right).toBe(window.innerWidth); - }); - - it('should get the viewport bounds when the page is scrolled', () => { - document.body.appendChild(veryLargeElement); - - scrollTo(1500, 2000); - - const bounds = ruler.getViewportRect(); - - // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the - // body causes karma's iframe for the test to stretch to fit that content once we attempt to - // scroll the page. Setting width / height / maxWidth / maxHeight on the iframe does not - // successfully constrain its size. As such, skip assertions in environments where the - // window size has changed since the start of the test. - if (window.innerWidth > startingWindowWidth || window.innerHeight > startingWindowHeight) { - document.body.removeChild(veryLargeElement); - return; - } - - expect(bounds.top).toBe(2000); - expect(bounds.left).toBe(1500); - expect(bounds.bottom).toBe(2000 + window.innerHeight); - expect(bounds.right).toBe(1500 + window.innerWidth); - - document.body.removeChild(veryLargeElement); - }); - - it('should get the bounds based on client coordinates when the page is pinch-zoomed', () => { - // There is no API to make the browser pinch-zoom, so there's no real way to automate - // tests for this behavior. Leaving this test here as documentation for the behavior. - }); - - it('should get the scroll position when the page is not scrolled', () => { - const scrollPos = ruler.getViewportScrollPosition(); - expect(scrollPos.top).toBe(0); - expect(scrollPos.left).toBe(0); - }); - - it('should get the scroll position when the page is scrolled', () => { - document.body.appendChild(veryLargeElement); - - scrollTo(1500, 2000); - - // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the - // body causes karma's iframe for the test to stretch to fit that content once we attempt to - // scroll the page. Setting width / height / maxWidth / maxHeight on the iframe does not - // successfully constrain its size. As such, skip assertions in environments where the - // window size has changed since the start of the test. - if (window.innerWidth > startingWindowWidth || window.innerHeight > startingWindowHeight) { - document.body.removeChild(veryLargeElement); - return; - } - - const scrollPos = ruler.getViewportScrollPosition(); - expect(scrollPos.top).toBe(2000); - expect(scrollPos.left).toBe(1500); - - document.body.removeChild(veryLargeElement); - }); - - describe('changed event', () => { - it('should dispatch an event when the window is resized', () => { - const spy = jasmine.createSpy('viewport changed spy'); - const subscription = ruler.change(0).subscribe(spy); - - dispatchFakeEvent(window, 'resize'); - expect(spy).toHaveBeenCalled(); - subscription.unsubscribe(); - }); - - it('should dispatch an event when the orientation is changed', () => { - const spy = jasmine.createSpy('viewport changed spy'); - const subscription = ruler.change(0).subscribe(spy); - - dispatchFakeEvent(window, 'orientationchange'); - expect(spy).toHaveBeenCalled(); - subscription.unsubscribe(); - }); - - it('should be able to throttle the callback', fakeAsync(() => { - const spy = jasmine.createSpy('viewport changed spy'); - const subscription = ruler.change(1337).subscribe(spy); - - dispatchFakeEvent(window, 'resize'); - expect(spy).not.toHaveBeenCalled(); - - tick(1337); - - expect(spy).toHaveBeenCalledTimes(1); - subscription.unsubscribe(); - })); - - it('should run the resize event outside the NgZone', () => { - const spy = jasmine.createSpy('viewport changed spy'); - const subscription = ruler.change(0).subscribe(() => spy(NgZone.isInAngularZone())); - - dispatchFakeEvent(window, 'resize'); - expect(spy).toHaveBeenCalledWith(false); - subscription.unsubscribe(); - }); - - }); -}); diff --git a/packages/cdk/scrolling/viewport-ruler.ts b/packages/cdk/scrolling/viewport-ruler.ts deleted file mode 100644 index 792fa5cfc..000000000 --- a/packages/cdk/scrolling/viewport-ruler.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { Injectable, NgZone, OnDestroy, Optional, SkipSelf } from '@angular/core'; -import { Platform } from '@ptsecurity/cdk/platform'; -import { merge, of as observableOf, fromEvent, Observable, Subscription } from 'rxjs'; -import { auditTime } from 'rxjs/operators'; - - -/** Time in ms to throttle the resize events by default. */ -export const DEFAULT_RESIZE_TIME = 20; - -export interface ViewportScrollPosition { - top: number; - left: number; -} - -/** - * Simple utility for getting the bounds of the browser viewport. - * @docs-private - */ -@Injectable({providedIn: 'root'}) -export class ViewportRuler implements OnDestroy { - /** Cached viewport dimensions. */ - private _viewportSize: {width: number; height: number}; - - /** Stream of viewport change events. */ - private _change: Observable; - - /** Subscription to streams that invalidate the cached viewport dimensions. */ - private _invalidateCache: Subscription; - - constructor(private _platform: Platform, ngZone: NgZone) { - ngZone.runOutsideAngular(() => { - this._change = _platform.isBrowser ? - merge(fromEvent(window, 'resize'), fromEvent(window, 'orientationchange')) : - observableOf(); - - // Note that we need to do the subscription inside `runOutsideAngular` - // since subscribing is what causes the event listener to be added. - this._invalidateCache = this.change().subscribe(() => this._updateViewportSize()); - }); - } - - ngOnDestroy() { - this._invalidateCache.unsubscribe(); - } - - /** Returns the viewport's width and height. */ - getViewportSize(): Readonly<{width: number, height: number}> { - if (!this._viewportSize) { - this._updateViewportSize(); - } - - const output = {width: this._viewportSize.width, height: this._viewportSize.height}; - - // If we're not on a browser, don't cache the size since it'll be mocked out anyway. - if (!this._platform.isBrowser) { - this._viewportSize = null!; //tslint:disable-line - } - - return output; - } - - /** Gets a ClientRect for the viewport's bounds. */ - getViewportRect(): ClientRect { - // Use the document element's bounding rect rather than the window scroll properties - // (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE where window scroll - // properties and client coordinates (boundingClientRect, clientX/Y, etc.) are in different - // conceptual viewports. Under most circumstances these viewports are equivalent, but they - // can disagree when the page is pinch-zoomed (on devices that support touch). - // See https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4 - // We use the documentElement instead of the body because, by default (without a css reset) - // browsers typically give the document body an 8px margin, which is not included in - // getBoundingClientRect(). - const scrollPosition = this.getViewportScrollPosition(); - const {width, height} = this.getViewportSize(); - - return { - top: scrollPosition.top, - left: scrollPosition.left, - bottom: scrollPosition.top + height, - right: scrollPosition.left + width, - height, - width - }; - } - - /** Gets the (top, left) scroll position of the viewport. */ - getViewportScrollPosition(): ViewportScrollPosition { - // While we can get a reference to the fake document - // during SSR, it doesn't have getBoundingClientRect. - if (!this._platform.isBrowser) { - return {top: 0, left: 0}; - } - - // The top-left-corner of the viewport is determined by the scroll position of the document - // body, normally just (scrollLeft, scrollTop). However, Chrome and Firefox disagree about - // whether `document.body` or `document.documentElement` is the scrolled element, so reading - // `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding rect of - // `document.documentElement` works consistently, where the `top` and `left` values will - // equal negative the scroll position. - const documentElement = document.documentElement!; - const documentRect = documentElement.getBoundingClientRect(); - - const top = -documentRect.top || document.body.scrollTop || window.scrollY || - document.documentElement!.scrollTop || 0; - - const left = -documentRect.left || document.body.scrollLeft || window.scrollX || - document.documentElement!.scrollLeft || 0; - - return { top, left }; - } - - /** - * Returns a stream that emits whenever the size of the viewport changes. - * @param throttleTime Time in milliseconds to throttle the stream. - */ - change(throttleTime: number = DEFAULT_RESIZE_TIME): Observable { - return throttleTime > 0 ? this._change.pipe(auditTime(throttleTime)) : this._change; - } - - /** Updates the cached viewport size. */ - private _updateViewportSize() { - this._viewportSize = this._platform.isBrowser ? - {width: window.innerWidth, height: window.innerHeight} : - {width: 0, height: 0}; - } -} - - -/** @docs-private - * @deprecated - */ -export function VIEWPORT_RULER_PROVIDER_FACTORY(parentRuler: ViewportRuler, - platform: Platform, - ngZone: NgZone) { - return parentRuler || new ViewportRuler(platform, ngZone); -} - -/** @docs-private @deprecated @deletion-target 7.0.0 */ -export const VIEWPORT_RULER_PROVIDER = { - // If there is already a ViewportRuler available, use that. Otherwise, provide a new one. - provide: ViewportRuler, - deps: [[new Optional(), new SkipSelf(), ViewportRuler], Platform, NgZone], - useFactory: VIEWPORT_RULER_PROVIDER_FACTORY -}; diff --git a/packages/cdk/scrolling/virtual-for-of.ts b/packages/cdk/scrolling/virtual-for-of.ts deleted file mode 100644 index e2c496422..000000000 --- a/packages/cdk/scrolling/virtual-for-of.ts +++ /dev/null @@ -1,390 +0,0 @@ -import { - Directive, - DoCheck, - EmbeddedViewRef, - Input, - IterableChangeRecord, - IterableChanges, - IterableDiffer, - IterableDiffers, - NgIterable, - NgZone, - OnDestroy, - SkipSelf, - TemplateRef, - TrackByFunction, - ViewContainerRef -} from '@angular/core'; - -import { Observable, Subject, of as observableOf } from 'rxjs'; -import { pairwise, shareReplay, startWith, switchMap, takeUntil } from 'rxjs/operators'; - -import { - ArrayDataSource, - ICollectionViewer, - DataSource, - ListRange, - isDataSource -} from '@ptsecurity/cdk/collections'; - -import { CdkVirtualScrollViewport } from './virtual-scroll-viewport'; - - -/** The context for an item rendered by `CdkVirtualForOf` */ -export type CdkVirtualForOfContext = { - /** The item value. */ - $implicit: T; - /** The DataSource, Observable, or NgIterable that was passed to *cdkVirtualFor. */ - cdkVirtualForOf: DataSource | Observable | NgIterable; - /** The index of the item in the DataSource. */ - index: number; - /** The number of items in the DataSource. */ - count: number; - /** Whether this is the first item in the DataSource. */ - first: boolean; - /** Whether this is the last item in the DataSource. */ - last: boolean; - /** Whether the index is even. */ - even: boolean; - /** Whether the index is odd. */ - odd: boolean; -}; - - -/** Helper to extract size from a DOM Node. */ -function getSize(orientation: 'horizontal' | 'vertical', node: Node): number { - const el = node as Element; - - if (!el.getBoundingClientRect) { - return 0; - } - const rect = el.getBoundingClientRect(); - - return orientation === 'horizontal' ? rect.width : rect.height; -} - - -/** - * A directive similar to `ngForOf` to be used for rendering data inside a virtual scrolling - * container. - */ -@Directive({ - selector: '[cdkVirtualFor][cdkVirtualForOf]', -}) -export class CdkVirtualForOf implements ICollectionViewer, DoCheck, OnDestroy { - /** Emits when the rendered view of the data changes. */ - viewChange = new Subject(); - - /** Subject that emits when a new DataSource instance is given. */ - private _dataSourceChanges = new Subject>(); - - /** The DataSource to display. */ - @Input() - get cdkVirtualForOf(): DataSource | Observable | NgIterable { - return this._cdkVirtualForOf; - } - set cdkVirtualForOf(value: DataSource | Observable | NgIterable) { - this._cdkVirtualForOf = value; - const ds = isDataSource(value) ? value : - // Slice the value if its an NgIterable to ensure we're working with an array. - new ArrayDataSource( - value instanceof Observable ? value : Array.prototype.slice.call(value || [])); - this._dataSourceChanges.next(ds); - } - _cdkVirtualForOf: DataSource | Observable | NgIterable; - - /** - * The `TrackByFunction` to use for tracking changes. The `TrackByFunction` takes the index and - * the item and produces a value to be used as the item's identity when tracking changes. - */ - @Input() - get cdkVirtualForTrackBy(): TrackByFunction | undefined { - return this._cdkVirtualForTrackBy; - } - set cdkVirtualForTrackBy(fn: TrackByFunction | undefined) { - this._needsUpdate = true; - this._cdkVirtualForTrackBy = fn ? - (index, item) => fn(index + (this._renderedRange ? this._renderedRange.start : 0), item) : - undefined; - } - private _cdkVirtualForTrackBy: TrackByFunction | undefined; - - /** The template used to stamp out new elements. */ - @Input() - set cdkVirtualForTemplate(value: TemplateRef>) { - if (value) { - this._needsUpdate = true; - this._template = value; - } - } - - /** - * The size of the cache used to store templates that are not being used for re-use later. - * Setting the cache size to `0` will disable caching. Defaults to 20 templates. - */ - @Input() cdkVirtualForTemplateCacheSize: number = 20; - - /** Emits whenever the data in the current DataSource changes. */ - dataStream: Observable> = this._dataSourceChanges - .pipe( - // Start off with null `DataSource`. - startWith(null!), - // Bundle up the previous and current data sources so we can work with both. - pairwise(), - // Use `_changeDataSource` to disconnect from the previous data source and connect to the - // new one, passing back a stream of data changes which we run through `switchMap` to give - // us a data stream that emits the latest data from whatever the current `DataSource` is. - switchMap(([prev, cur]) => this._changeDataSource(prev, cur)), - // Replay the last emitted data when someone subscribes. - shareReplay(1)); - - /** The differ used to calculate changes to the data. */ - private _differ: IterableDiffer | null = null; - - /** The most recent data emitted from the DataSource. */ - private _data: T[] | ReadonlyArray; - - /** The currently rendered items. */ - private _renderedItems: T[]; - - /** The currently rendered range of indices. */ - private _renderedRange: ListRange; - - /** - * The template cache used to hold on ot template instancess that have been stamped out, but don't - * currently need to be rendered. These instances will be reused in the future rather than - * stamping out brand new ones. - */ - private _templateCache: EmbeddedViewRef>[] = []; - - /** Whether the rendered data should be updated during the next ngDoCheck cycle. */ - private _needsUpdate = false; - - private _destroyed = new Subject(); - - constructor( - /** The view container to add items to. */ - private _viewContainerRef: ViewContainerRef, - /** The template to use when stamping out new items. */ - private _template: TemplateRef>, - /** The set of available differs. */ - private _differs: IterableDiffers, - /** The virtual scrolling viewport that these items are being rendered in. */ - @SkipSelf() private _viewport: CdkVirtualScrollViewport, - ngZone: NgZone) { - this.dataStream.subscribe(data => { - this._data = data; - this._onRenderedDataChange(); - }); - this._viewport.renderedRangeStream.pipe(takeUntil(this._destroyed)).subscribe(range => { - this._renderedRange = range; - ngZone.run(() => this.viewChange.next(this._renderedRange)); - this._onRenderedDataChange(); - }); - this._viewport.attach(this); - } - - /** - * Measures the combined size (width for horizontal orientation, height for vertical) of all items - * in the specified range. Throws an error if the range includes items that are not currently - * rendered. - */ - measureRangeSize(range: ListRange, orientation: 'horizontal' | 'vertical'): number { - if (range.start >= range.end) { - return 0; - } - if (range.start < this._renderedRange.start || range.end > this._renderedRange.end) { - throw Error(`Error: attempted to measure an item that isn't rendered.`); - } - - // The index into the list of rendered views for the first item in the range. - const renderedStartIndex = range.start - this._renderedRange.start; - // The length of the range we're measuring. - const rangeLen = range.end - range.start; - - // Loop over all root nodes for all items in the range and sum up their size. - let totalSize = 0; - let i = rangeLen; - while (i--) { - const view = this._viewContainerRef.get(i + renderedStartIndex) as - EmbeddedViewRef> | null; - let j = view ? view.rootNodes.length : 0; - while (j--) { - totalSize += getSize(orientation, view!.rootNodes[j]); - } - } - - return totalSize; - } - - ngDoCheck() { - if (this._differ && this._needsUpdate) { - // TODO: We should differentiate needs update due to scrolling and a new portion of - // this list being rendered (can use simpler algorithm) vs needs update due to data actually - // changing (need to do this diff). - const changes = this._differ.diff(this._renderedItems); - if (!changes) { - this._updateContext(); - } else { - this._applyChanges(changes); - } - this._needsUpdate = false; - } - } - - ngOnDestroy() { - this._viewport.detach(); - - this._dataSourceChanges.next(); - this._dataSourceChanges.complete(); - this.viewChange.complete(); - - this._destroyed.next(); - this._destroyed.complete(); - - for (let view of this._templateCache) { - view.destroy(); - } - } - - /** React to scroll state changes in the viewport. */ - private _onRenderedDataChange() { - if (!this._renderedRange) { - return; - } - this._renderedItems = this._data.slice(this._renderedRange.start, this._renderedRange.end); - if (!this._differ) { - this._differ = this._differs.find(this._renderedItems).create(this.cdkVirtualForTrackBy); - } - this._needsUpdate = true; - } - - /** Swap out one `DataSource` for another. */ - private _changeDataSource(oldDs: DataSource | null, newDs: DataSource | null): - Observable> { - - if (oldDs) { - oldDs.disconnect(this); - } - - this._needsUpdate = true; - - return newDs ? newDs.connect(this) : observableOf(); - } - - /** Update the `CdkVirtualForOfContext` for all views. */ - private _updateContext() { - const count = this._data.length; - let i = this._viewContainerRef.length; - while (i--) { - let view = this._viewContainerRef.get(i) as EmbeddedViewRef>; - view.context.index = this._renderedRange.start + i; - view.context.count = count; - this._updateComputedContextProperties(view.context); - view.detectChanges(); - } - } - - /** Apply changes to the DOM. */ - private _applyChanges(changes: IterableChanges) { - // Rearrange the views to put them in the right location. - changes.forEachOperation((record: IterableChangeRecord, - adjustedPreviousIndex: number | null, - currentIndex: number | null) => { - if (record.previousIndex == null) { // Item added. - const view = this._insertViewForNewItem(currentIndex!); - view.context.$implicit = record.item; - } else if (currentIndex == null) { // Item removed. - this._cacheView(this._detachView(adjustedPreviousIndex !)); - } else { // Item moved. - const view = this._viewContainerRef.get(adjustedPreviousIndex!) as - EmbeddedViewRef>; - this._viewContainerRef.move(view, currentIndex); - view.context.$implicit = record.item; - } - }); - - // Update $implicit for any items that had an identity change. - changes.forEachIdentityChange((record: IterableChangeRecord) => { - const view = this._viewContainerRef.get(record.currentIndex!) as - EmbeddedViewRef>; - view.context.$implicit = record.item; - }); - - // Update the context variables on all items. - const count = this._data.length; - let i = this._viewContainerRef.length; - while (i--) { - const view = this._viewContainerRef.get(i) as EmbeddedViewRef>; - view.context.index = this._renderedRange.start + i; - view.context.count = count; - this._updateComputedContextProperties(view.context); - } - } - - /** Cache the given detached view. */ - private _cacheView(view: EmbeddedViewRef>) { - if (this._templateCache.length < this.cdkVirtualForTemplateCacheSize) { - this._templateCache.push(view); - } else { - const index = this._viewContainerRef.indexOf(view); - - // It's very unlikely that the index will ever be -1, but just in case, - // destroy the view on its own, otherwise destroy it through the - // container to ensure that all the references are removed. - if (index === -1) { - view.destroy(); - } else { - this._viewContainerRef.remove(index); - } - } - } - - /** Inserts a view for a new item, either from the cache or by creating a new one. */ - private _insertViewForNewItem(index: number): EmbeddedViewRef> { - return this._insertViewFromCache(index) || this._createEmbeddedViewAt(index); - } - - /** Update the computed properties on the `CdkVirtualForOfContext`. */ - private _updateComputedContextProperties(context: CdkVirtualForOfContext) { - context.first = context.index === 0; - context.last = context.index === context.count - 1; - context.even = context.index % 2 === 0; - context.odd = !context.even; - } - - /** Creates a new embedded view and moves it to the given index */ - private _createEmbeddedViewAt(index: number): EmbeddedViewRef> { - const view = this._viewContainerRef.createEmbeddedView(this._template, { - $implicit: null!, - cdkVirtualForOf: this._cdkVirtualForOf, - index: -1, - count: -1, - first: false, - last: false, - odd: false, - even: false - }); - if (index < this._viewContainerRef.length) { - this._viewContainerRef.move(view, index); - } - - return view; - } - - /** Inserts a recycled view from the cache at the given index. */ - private _insertViewFromCache(index: number): EmbeddedViewRef>|null { - const cachedView = this._templateCache.pop(); - if (cachedView) { - this._viewContainerRef.insert(cachedView, index); - } - - return cachedView || null; - } - - /** Detaches the embedded view at the given index. */ - private _detachView(index: number): EmbeddedViewRef> { - return this._viewContainerRef.detach(index) as - EmbeddedViewRef>; - } -} diff --git a/packages/cdk/scrolling/virtual-scroll-strategy.ts b/packages/cdk/scrolling/virtual-scroll-strategy.ts deleted file mode 100644 index 74bd711b2..000000000 --- a/packages/cdk/scrolling/virtual-scroll-strategy.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { InjectionToken } from '@angular/core'; -import { Observable } from 'rxjs'; - -import { CdkVirtualScrollViewport } from './virtual-scroll-viewport'; - - -/** The injection token used to specify the virtual scrolling strategy. */ -export const VIRTUAL_SCROLL_STRATEGY = - new InjectionToken('VIRTUAL_SCROLL_STRATEGY'); - - -/** A strategy that dictates which items should be rendered in the viewport. */ -export interface VirtualScrollStrategy { - /** Emits when the index of the first element visible in the viewport changes. */ - scrolledIndexChange: Observable; - - /** - * Attaches this scroll strategy to a viewport. - * @param viewport The viewport to attach this strategy to. - */ - attach(viewport: CdkVirtualScrollViewport): void; - - /** Detaches this scroll strategy from the currently attached viewport. */ - detach(): void; - - /** Called when the viewport is scrolled (debounced using requestAnimationFrame). */ - onContentScrolled(): void; - - /** Called when the length of the data changes. */ - onDataLengthChanged(): void; - - /** Called when the range of items rendered in the DOM has changed. */ - onContentRendered(): void; - - /** Called when the offset of the rendered items changed. */ - onRenderedOffsetChanged(): void; - - /** - * Scroll to the offset for the given index. - * @param index The index of the element to scroll to. - * @param behavior The ScrollBehavior to use when scrolling. - */ - scrollToIndex(index: number, behavior: ScrollBehavior): void; -} diff --git a/packages/cdk/scrolling/virtual-scroll-viewport.html b/packages/cdk/scrolling/virtual-scroll-viewport.html deleted file mode 100644 index 4da0d8085..000000000 --- a/packages/cdk/scrolling/virtual-scroll-viewport.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- -
- -
diff --git a/packages/cdk/scrolling/virtual-scroll-viewport.scss b/packages/cdk/scrolling/virtual-scroll-viewport.scss deleted file mode 100644 index 8f127ce88..000000000 --- a/packages/cdk/scrolling/virtual-scroll-viewport.scss +++ /dev/null @@ -1,87 +0,0 @@ -// When elements such as `` or `
  • ` are repeated inside the cdk-virtual-scroll-viewport, -// their container element (e.g. ``, `
      `, etc.) needs to be placed in the viewport as -// well. We reset some properties here to prevent these container elements from introducing -// additional space that would throw off the the scrolling calculations. -@mixin _cdk-virtual-scroll-clear-container-space($direction) { - $start: if($direction == horizontal, 'left', 'top'); - $end: if($direction == horizontal, 'right', 'bottom'); - - & > dl:not([cdkVirtualFor]), - & > ol:not([cdkVirtualFor]), - & > table:not([cdkVirtualFor]), - & > ul:not([cdkVirtualFor]) { - padding: { - #{$start}: 0; - #{$end}: 0; - } - margin: { - #{$start}: 0; - #{$end}: 0; - } - border: { - #{$start}-width: 0; - #{$end}-width: 0; - } - outline: none; - } -} - - -// Scrolling container. -cdk-virtual-scroll-viewport { - display: block; - position: relative; - overflow: auto; - contain: strict; - transform: translateZ(0); - will-change: scroll-position; - -webkit-overflow-scrolling: touch; -} - -// Wrapper element for the rendered content. This element will be transformed to push the rendered -// content to its correct offset in the data set as a whole. -.cdk-virtual-scroll-content-wrapper { - position: absolute; - top: 0; - left: 0; - contain: content; - - // Note: We can't put `will-change: transform;` here because it causes Safari to not update the - // viewport's `scrollHeight` when the spacer's transform changes. - - [dir='rtl'] & { - right: 0; - left: auto; - } -} - -.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper { - min-height: 100%; - @include _cdk-virtual-scroll-clear-container-space(horizontal); -} - -.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper { - min-width: 100%; - @include _cdk-virtual-scroll-clear-container-space(vertical); -} - -// Spacer element that whose width or height will be adjusted to match the size of the entire data -// set if it were rendered all at once. This ensures that the scrollable content region is the -// correct size. -.cdk-virtual-scroll-spacer { - position: absolute; - top: 0; - left: 0; - height: 1px; - width: 1px; - transform-origin: 0 0; - - // Note: We can't put `will-change: transform;` here because it causes Safari to not update the - // viewport's `scrollHeight` when the spacer's transform changes. - - [dir='rtl'] & { - right: 0; - left: auto; - transform-origin: 100% 0; - } -} diff --git a/packages/cdk/scrolling/virtual-scroll-viewport.spec.ts b/packages/cdk/scrolling/virtual-scroll-viewport.spec.ts deleted file mode 100644 index a65271d86..000000000 --- a/packages/cdk/scrolling/virtual-scroll-viewport.spec.ts +++ /dev/null @@ -1,942 +0,0 @@ -import { - Component, - Input, - NgZone, - TrackByFunction, - ViewChild, - ViewEncapsulation -} from '@angular/core'; -import { ComponentFixture, fakeAsync, flush, inject, TestBed } from '@angular/core/testing'; -import { ArrayDataSource } from '@ptsecurity/cdk/collections'; -import { - CdkVirtualForOf, - CdkVirtualScrollViewport, - ScrollDispatcher, - ScrollingModule -} from '@ptsecurity/cdk/scrolling'; -import { dispatchFakeEvent } from '@ptsecurity/cdk/testing'; -import { animationFrameScheduler, Subject } from 'rxjs'; - - -// tslint:disable:no-magic-numbers -describe('CdkVirtualScrollViewport', () => { - describe('with FixedSizeVirtualScrollStrategy', () => { - let fixture: ComponentFixture; - let testComponent: FixedSizeVirtualScroll; - let viewport: CdkVirtualScrollViewport; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ScrollingModule], - declarations: [FixedSizeVirtualScroll] - }).compileComponents(); - - fixture = TestBed.createComponent(FixedSizeVirtualScroll); - testComponent = fixture.componentInstance; - viewport = testComponent.viewport; - }); - - it('should render initial state', fakeAsync(() => { - finishInit(fixture); - - const contentWrapper = - viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper')!; - expect(contentWrapper.children.length) - .toBe(4, 'should render 4 50px items to fill 200px space'); - })); - - it('should get the data length', fakeAsync(() => { - finishInit(fixture); - - expect(viewport.getDataLength()).toBe(testComponent.items.length); - })); - - it('should get the viewport size', fakeAsync(() => { - finishInit(fixture); - - expect(viewport.getViewportSize()).toBe(testComponent.viewportSize); - })); - - it('should update viewport size', fakeAsync(() => { - testComponent.viewportSize = 300; - fixture.detectChanges(); - flush(); - viewport.checkViewportSize(); - expect(viewport.getViewportSize()).toBe(300); - - testComponent.viewportSize = 500; - fixture.detectChanges(); - flush(); - viewport.checkViewportSize(); - expect(viewport.getViewportSize()).toBe(500); - })); - - it('should get the rendered range', fakeAsync(() => { - finishInit(fixture); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 4}, 'should render the first 4 50px items to fill 200px space'); - })); - - it('should get the rendered content offset', fakeAsync(() => { - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize + 5); - fixture.detectChanges(); - flush(); - - expect(viewport.getOffsetToRenderedContentStart()).toBe(testComponent.itemSize, - 'should have 50px offset since first 50px item is not rendered'); - })); - - it('should get the scroll offset', fakeAsync(() => { - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize + 5); - fixture.detectChanges(); - flush(); - - expect(viewport.measureScrollOffset()).toBe(testComponent.itemSize + 5); - })); - - it('should get the rendered content size', fakeAsync(() => { - finishInit(fixture); - - expect(viewport.measureRenderedContentSize()) - .toBe(testComponent.viewportSize, - 'should render 4 50px items with combined size of 200px to fill 200px space'); - })); - - it('should measure range size', fakeAsync(() => { - finishInit(fixture); - - expect(viewport.measureRangeSize({start: 1, end: 3})) - .toBe(testComponent.itemSize * 2, 'combined size of 2 50px items should be 100px'); - })); - - it('should set total content size', fakeAsync(() => { - finishInit(fixture); - - viewport.setTotalContentSize(10000); - flush(); - fixture.detectChanges(); - - expect(viewport.elementRef.nativeElement.scrollHeight).toBe(10000); - })); - - it('should set total content size in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - viewport.setTotalContentSize(10000); - flush(); - fixture.detectChanges(); - - expect(viewport.elementRef.nativeElement.scrollWidth).toBe(10000); - })); - - it('should set a class based on the orientation', fakeAsync(() => { - finishInit(fixture); - const viewportElement: HTMLElement = - fixture.nativeElement.querySelector('.cdk-virtual-scroll-viewport'); - - expect(viewportElement.classList).toContain('cdk-virtual-scroll-orientation-vertical'); - - testComponent.orientation = 'horizontal'; - fixture.detectChanges(); - - expect(viewportElement.classList).toContain('cdk-virtual-scroll-orientation-horizontal'); - })); - - it('should set the vertical class if an invalid orientation is set', fakeAsync(() => { - testComponent.orientation = 'diagonal'; - finishInit(fixture); - const viewportElement: HTMLElement = - fixture.nativeElement.querySelector('.cdk-virtual-scroll-viewport'); - - expect(viewportElement.classList).toContain('cdk-virtual-scroll-orientation-vertical'); - })); - - it('should set rendered range', fakeAsync(() => { - finishInit(fixture); - viewport.setRenderedRange({start: 2, end: 3}); - fixture.detectChanges(); - flush(); - - const items = fixture.elementRef.nativeElement.querySelectorAll('.item'); - expect(items.length).toBe(1, 'Expected 1 item to be rendered'); - expect(items[0].innerText.trim()).toBe('2 - 2', 'Expected item with index 2 to be rendered'); - })); - - it('should set content offset to top of content', fakeAsync(() => { - finishInit(fixture); - viewport.setRenderedContentOffset(10, 'to-start'); - fixture.detectChanges(); - flush(); - - expect(viewport.getOffsetToRenderedContentStart()).toBe(10); - })); - - it('should set content offset to bottom of content', fakeAsync(() => { - finishInit(fixture); - const contentSize = viewport.measureRenderedContentSize(); - - expect(contentSize).toBeGreaterThan(0); - - viewport.setRenderedContentOffset(contentSize + 10, 'to-end'); - fixture.detectChanges(); - flush(); - - expect(viewport.getOffsetToRenderedContentStart()).toBe(10); - })); - - it('should scroll to offset', fakeAsync(() => { - finishInit(fixture); - viewport.scrollToOffset(testComponent.itemSize * 2); - - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect(viewport.measureScrollOffset()).toBe(testComponent.itemSize * 2); - expect(viewport.getRenderedRange()).toEqual({start: 2, end: 6}); - })); - - it('should scroll to index', fakeAsync(() => { - finishInit(fixture); - viewport.scrollToIndex(2); - - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect(viewport.measureScrollOffset()).toBe(testComponent.itemSize * 2); - expect(viewport.getRenderedRange()).toEqual({start: 2, end: 6}); - })); - - it('should scroll to offset in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - viewport.scrollToOffset(testComponent.itemSize * 2); - - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect(viewport.measureScrollOffset()).toBe(testComponent.itemSize * 2); - expect(viewport.getRenderedRange()).toEqual({start: 2, end: 6}); - })); - - it('should scroll to index in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - viewport.scrollToIndex(2); - - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect(viewport.measureScrollOffset()).toBe(testComponent.itemSize * 2); - expect(viewport.getRenderedRange()).toEqual({start: 2, end: 6}); - })); - - it('should output scrolled index', fakeAsync(() => { - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize * 2 - 1); - fixture.detectChanges(); - flush(); - - expect(testComponent.scrolledToIndex).toBe(1); - - triggerScroll(viewport, testComponent.itemSize * 2); - fixture.detectChanges(); - flush(); - - expect(testComponent.scrolledToIndex).toBe(2); - })); - - it('should update viewport as user scrolls down', fakeAsync(() => { - finishInit(fixture); - - const maxOffset = - testComponent.itemSize * testComponent.items.length - testComponent.viewportSize; - - for (let offset = 1; offset <= maxOffset; offset += 10) { - triggerScroll(viewport, offset); - fixture.detectChanges(); - flush(); - - const expectedRange = { - start: Math.floor(offset / testComponent.itemSize), - end: Math.ceil((offset + testComponent.viewportSize) / testComponent.itemSize) - }; - expect(viewport.getRenderedRange()) - .toEqual(expectedRange, - `rendered range should match expected value at scroll offset ${offset}`); - expect(viewport.getOffsetToRenderedContentStart()) - .toBe(expectedRange.start * testComponent.itemSize, - `rendered content offset should match expected value at scroll offset ${offset}`); - expect(viewport.measureRenderedContentSize()) - .toBe((expectedRange.end - expectedRange.start) * testComponent.itemSize, - `rendered content size should match expected value at offset ${offset}`); - } - })); - - it('should update viewport as user scrolls up', fakeAsync(() => { - finishInit(fixture); - - const maxOffset = - testComponent.itemSize * testComponent.items.length - testComponent.viewportSize; - - for (let offset = maxOffset - 1; offset >= 0; offset -= 10) { - triggerScroll(viewport, offset); - fixture.detectChanges(); - flush(); - - const expectedRange = { - start: Math.floor(offset / testComponent.itemSize), - end: Math.ceil((offset + testComponent.viewportSize) / testComponent.itemSize) - }; - expect(viewport.getRenderedRange()) - .toEqual(expectedRange, - `rendered range should match expected value at scroll offset ${offset}`); - expect(viewport.getOffsetToRenderedContentStart()) - .toBe(expectedRange.start * testComponent.itemSize, - `rendered content offset should match expected value at scroll offset ${offset}`); - expect(viewport.measureRenderedContentSize()) - .toBe((expectedRange.end - expectedRange.start) * testComponent.itemSize, - `rendered content size should match expected value at offset ${offset}`); - } - })); - - it('should render buffer element at the end when scrolled to the top', fakeAsync(() => { - testComponent.minBufferPx = testComponent.itemSize; - testComponent.maxBufferPx = testComponent.itemSize; - finishInit(fixture); - - expect(viewport.getRenderedRange()).toEqual({start: 0, end: 5}, - 'should render the first 5 50px items to fill 200px space, plus one buffer element at' + - ' the end'); - })); - - it('should render buffer element at the start and end when scrolled to the middle', - fakeAsync(() => { - testComponent.minBufferPx = testComponent.itemSize; - testComponent.maxBufferPx = testComponent.itemSize; - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize * 2); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()).toEqual({start: 1, end: 7}, - 'should render 6 50px items to fill 200px space, plus one buffer element at the' + - ' start and end'); - })); - - it('should render buffer element at the start when scrolled to the bottom', fakeAsync(() => { - testComponent.minBufferPx = testComponent.itemSize; - testComponent.maxBufferPx = testComponent.itemSize; - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize * 6); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()).toEqual({start: 5, end: 10}, - 'should render the last 5 50px items to fill 200px space, plus one buffer element at' + - ' the start'); - })); - - it('should handle dynamic item size', fakeAsync(() => { - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize * 2); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 2, end: 6}, 'should render 4 50px items to fill 200px space'); - - testComponent.itemSize *= 2; - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 1, end: 3}, 'should render 2 100px items to fill 200px space'); - })); - - it('should handle dynamic buffer size', fakeAsync(() => { - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize * 2); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 2, end: 6}, 'should render 4 50px items to fill 200px space'); - - testComponent.minBufferPx = testComponent.itemSize; - testComponent.maxBufferPx = testComponent.itemSize; - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 1, end: 7}, 'should expand to 1 buffer element on each side'); - })); - - it('should handle dynamic item array', fakeAsync(() => { - finishInit(fixture); - triggerScroll(viewport, testComponent.itemSize * 6); - fixture.detectChanges(); - flush(); - - expect(viewport.getOffsetToRenderedContentStart()) - .toBe(testComponent.itemSize * 6, 'should be scrolled to bottom of 10 item list'); - - testComponent.items = Array(5).fill(0); - fixture.detectChanges(); - flush(); - - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect(viewport.getOffsetToRenderedContentStart()) - .toBe(testComponent.itemSize, 'should be scrolled to bottom of 5 item list'); - })); - - it('should update viewport as user scrolls right in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - const maxOffset = - testComponent.itemSize * testComponent.items.length - testComponent.viewportSize; - - for (let offset = 1; offset <= maxOffset; offset += 10) { - triggerScroll(viewport, offset); - fixture.detectChanges(); - flush(); - - const expectedRange = { - start: Math.floor(offset / testComponent.itemSize), - end: Math.ceil((offset + testComponent.viewportSize) / testComponent.itemSize) - }; - expect(viewport.getRenderedRange()) - .toEqual(expectedRange, - `rendered range should match expected value at scroll offset ${offset}`); - expect(viewport.getOffsetToRenderedContentStart()) - .toBe(expectedRange.start * testComponent.itemSize, - `rendered content offset should match expected value at scroll offset ${offset}`); - expect(viewport.measureRenderedContentSize()) - .toBe((expectedRange.end - expectedRange.start) * testComponent.itemSize, - `rendered content size should match expected value at offset ${offset}`); - } - })); - - it('should update viewport as user scrolls left in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - const maxOffset = - testComponent.itemSize * testComponent.items.length - testComponent.viewportSize; - - for (let offset = maxOffset - 1; offset >= 0; offset -= 10) { - triggerScroll(viewport, offset); - fixture.detectChanges(); - flush(); - - const expectedRange = { - start: Math.floor(offset / testComponent.itemSize), - end: Math.ceil((offset + testComponent.viewportSize) / testComponent.itemSize) - }; - expect(viewport.getRenderedRange()) - .toEqual(expectedRange, - `rendered range should match expected value at scroll offset ${offset}`); - expect(viewport.getOffsetToRenderedContentStart()) - .toBe(expectedRange.start * testComponent.itemSize, - `rendered content offset should match expected value at scroll offset ${offset}`); - expect(viewport.measureRenderedContentSize()) - .toBe((expectedRange.end - expectedRange.start) * testComponent.itemSize, - `rendered content size should match expected value at offset ${offset}`); - } - })); - - it('should work with an Observable', fakeAsync(() => { - const data = new Subject(); - testComponent.items = data as any; - finishInit(fixture); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 0}, 'no items should be rendered'); - - data.next([1, 2, 3]); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 3}, 'newly emitted items should be rendered'); - })); - - it('should work with a DataSource', fakeAsync(() => { - const data = new Subject(); - testComponent.items = new ArrayDataSource(data) as any; - finishInit(fixture); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 0}, 'no items should be rendered'); - - data.next([1, 2, 3]); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 3}, 'newly emitted items should be rendered'); - })); - - it('should disconnect from data source on destroy', fakeAsync(() => { - const data = new Subject(); - const dataSource = new ArrayDataSource(data); - - spyOn(dataSource, 'connect').and.callThrough(); - spyOn(dataSource, 'disconnect').and.callThrough(); - - testComponent.items = dataSource as any; - finishInit(fixture); - - expect(dataSource.connect).toHaveBeenCalled(); - - fixture.destroy(); - - expect(dataSource.disconnect).toHaveBeenCalled(); - })); - - - it('should trackBy value by default', fakeAsync(() => { - testComponent.items = []; - spyOn(testComponent.virtualForOf, '_detachView').and.callThrough(); - finishInit(fixture); - - testComponent.items = [0]; - fixture.detectChanges(); - flush(); - - expect(testComponent.virtualForOf._detachView).not.toHaveBeenCalled(); - - testComponent.items = [1]; - fixture.detectChanges(); - flush(); - - expect(testComponent.virtualForOf._detachView).toHaveBeenCalled(); - })); - - it('should trackBy index when specified', fakeAsync(() => { - testComponent.trackBy = (i) => i; - testComponent.items = []; - spyOn(testComponent.virtualForOf, '_detachView').and.callThrough(); - finishInit(fixture); - - testComponent.items = [0]; - fixture.detectChanges(); - flush(); - - expect(testComponent.virtualForOf._detachView).not.toHaveBeenCalled(); - - testComponent.items = [1]; - fixture.detectChanges(); - flush(); - - expect(testComponent.virtualForOf._detachView).not.toHaveBeenCalled(); - })); - - it('should recycle views when template cache is large enough to accommodate', fakeAsync(() => { - testComponent.trackBy = (i) => i; - const spy = spyOn(testComponent.virtualForOf, '_createEmbeddedViewAt') - .and.callThrough(); - - finishInit(fixture); - - // Should create views for the initial rendered items. - expect(testComponent.virtualForOf._createEmbeddedViewAt) - .toHaveBeenCalledTimes(4); - - spy.calls.reset(); - triggerScroll(viewport, 10); - fixture.detectChanges(); - flush(); - - // As we first start to scroll we need to create one more item. This is because the first item - // is still partially on screen and therefore can't be removed yet. At the same time a new - // item is now partially on the screen at the bottom and so a new view is needed. - expect(testComponent.virtualForOf._createEmbeddedViewAt) - .toHaveBeenCalledTimes(1); - - spy.calls.reset(); - const maxOffset = - testComponent.itemSize * testComponent.items.length - testComponent.viewportSize; - - for (let offset = 10; offset <= maxOffset; offset += 10) { - triggerScroll(viewport, offset); - fixture.detectChanges(); - flush(); - } - - // As we scroll through the rest of the items, no new views should be created, our existing 5 - // can just be recycled as appropriate. - expect(testComponent.virtualForOf._createEmbeddedViewAt) - .not.toHaveBeenCalled(); - })); - - it('should not recycle views when template cache is full', fakeAsync(() => { - testComponent.trackBy = (i) => i; - testComponent.templateCacheSize = 0; - const spy = - spyOn(testComponent.virtualForOf, '_createEmbeddedViewAt').and.callThrough(); - finishInit(fixture); - - // Should create views for the initial rendered items. - expect(testComponent.virtualForOf._createEmbeddedViewAt).toHaveBeenCalledTimes(4); - - spy.calls.reset(); - triggerScroll(viewport, 10); - fixture.detectChanges(); - flush(); - - // As we first start to scroll we need to create one more item. This is because the first item - // is still partially on screen and therefore can't be removed yet. At the same time a new - // item is now partially on the screen at the bottom and so a new view is needed. - expect(testComponent.virtualForOf._createEmbeddedViewAt).toHaveBeenCalledTimes(1); - - spy.calls.reset(); - const maxOffset = - testComponent.itemSize * testComponent.items.length - testComponent.viewportSize; - - for (let offset = 10; offset <= maxOffset; offset += 10) { - triggerScroll(viewport, offset); - fixture.detectChanges(); - flush(); - } - - // Since our template cache size is 0, as we scroll through the rest of the items, we need to - // create a new view for each one. - expect(testComponent.virtualForOf._createEmbeddedViewAt).toHaveBeenCalledTimes(5); - })); - - it('should render up to maxBufferPx when buffer dips below minBufferPx', fakeAsync(() => { - testComponent.minBufferPx = testComponent.itemSize; - testComponent.maxBufferPx = testComponent.itemSize * 2; - finishInit(fixture); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 6}, 'should have 2 buffer items initially'); - - triggerScroll(viewport, 50); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 6}, 'should not render additional buffer yet'); - - triggerScroll(viewport, 51); - fixture.detectChanges(); - flush(); - - expect(viewport.getRenderedRange()) - .toEqual({start: 0, end: 8}, 'should render 2 more buffer items'); - })); - - it('should throw if maxBufferPx is less than minBufferPx', fakeAsync(() => { - testComponent.minBufferPx = 100; - testComponent.maxBufferPx = 99; - expect(() => finishInit(fixture)).toThrow(); - })); - - it('should register and degregister with ScrollDispatcher', - fakeAsync(inject([ScrollDispatcher], (dispatcher: ScrollDispatcher) => { - spyOn(dispatcher, 'register').and.callThrough(); - spyOn(dispatcher, 'deregister').and.callThrough(); - finishInit(fixture); - expect(dispatcher.register).toHaveBeenCalledWith(testComponent.viewport); - fixture.destroy(); - expect(dispatcher.deregister).toHaveBeenCalledWith(testComponent.viewport); - }))); - - it('should emit on viewChange inside the Angular zone', fakeAsync(() => { - const zoneTest = jasmine.createSpy('zone test'); - testComponent.virtualForOf.viewChange.subscribe(() => zoneTest(NgZone.isInAngularZone())); - finishInit(fixture); - expect(zoneTest).toHaveBeenCalledWith(true); - })); - - it('should not throw when disposing of a view that will not fit in the cache', fakeAsync(() => { - finishInit(fixture); - testComponent.items = new Array(200).fill(0); - testComponent.templateCacheSize = 1; // Reduce the cache size to something we can easily hit. - fixture.detectChanges(); - flush(); - - expect(() => { - for (let i = 0; i < 50; i++) { - viewport.scrollToIndex(i); - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - } - }).not.toThrow(); - })); - - }); - - describe('with RTL direction', () => { - let fixture: ComponentFixture; - let testComponent: FixedSizeVirtualScrollWithRtlDirection; - let viewport: CdkVirtualScrollViewport; - let viewportEl: HTMLElement; - let contentWrapperEl: HTMLElement; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ScrollingModule], - declarations: [FixedSizeVirtualScrollWithRtlDirection] - }).compileComponents(); - - fixture = TestBed.createComponent(FixedSizeVirtualScrollWithRtlDirection); - testComponent = fixture.componentInstance; - viewport = testComponent.viewport; - viewportEl = viewport.elementRef.nativeElement; - contentWrapperEl = - viewportEl.querySelector('.cdk-virtual-scroll-content-wrapper') as HTMLElement; - }); - - it('should initially be scrolled all the way right and showing the first item in horizontal' + - ' mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - expect(viewport.measureScrollOffset('right')).toBe(0); - expect(contentWrapperEl.style.transform).toMatch(/translateX\(0(px)?\)/); - expect((contentWrapperEl.children[0] as HTMLElement).innerText.trim()).toBe('0 - 0'); - })); - - it('should scroll through items as user scrolls to the left in horizontal mode', - fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - triggerScroll(viewport, testComponent.itemSize * testComponent.items.length); - fixture.detectChanges(); - flush(); - - expect(contentWrapperEl.style.transform).toBe('translateX(-300px)'); - expect((contentWrapperEl.children[0] as HTMLElement).innerText.trim()).toBe('6 - 6'); - })); - - it('should interpret scrollToOffset amount as an offset from the right in horizontal mode', - fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - viewport.scrollToOffset(100); - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect(viewport.measureScrollOffset('right')).toBe(100); - })); - - it('should scroll to the correct index in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - viewport.scrollToIndex(2); - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect((contentWrapperEl.children[0] as HTMLElement).innerText.trim()).toBe('2 - 2'); - })); - - it('should emit the scrolled to index in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - expect(testComponent.scrolledToIndex).toBe(0); - - viewport.scrollToIndex(2); - triggerScroll(viewport); - fixture.detectChanges(); - flush(); - - expect(testComponent.scrolledToIndex).toBe(2); - })); - - it('should set total content size', fakeAsync(() => { - finishInit(fixture); - - viewport.setTotalContentSize(10000); - flush(); - fixture.detectChanges(); - - expect(viewport.elementRef.nativeElement.scrollHeight).toBe(10000); - })); - - it('should set total content size in horizontal mode', fakeAsync(() => { - testComponent.orientation = 'horizontal'; - finishInit(fixture); - - viewport.setTotalContentSize(10000); - flush(); - fixture.detectChanges(); - - expect(viewport.elementRef.nativeElement.scrollWidth).toBe(10000); - })); - }); - - describe('with no VirtualScrollStrategy', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ScrollingModule], - declarations: [VirtualScrollWithNoStrategy] - }).compileComponents(); - }); - - it('should fail on construction', fakeAsync(() => { - expect(() => TestBed.createComponent(VirtualScrollWithNoStrategy)).toThrowError( - 'Error: cdk-virtual-scroll-viewport requires the "itemSize" property to be set.'); - })); - }); -}); - - -/** Finish initializing the virtual scroll component at the beginning of a test. */ -function finishInit(fixture: ComponentFixture) { - // On the first cycle we render and measure the viewport. - fixture.detectChanges(); - flush(); - - // On the second cycle we render the items. - fixture.detectChanges(); - flush(); - - // Flush the initial fake scroll event. - animationFrameScheduler.flush(); - flush(); - fixture.detectChanges(); -} - -/** Trigger a scroll event on the viewport (optionally setting a new scroll offset). */ -function triggerScroll(viewport: CdkVirtualScrollViewport, offset?: number) { - if (offset !== undefined) { - viewport.scrollToOffset(offset); - } - dispatchFakeEvent(viewport.elementRef.nativeElement, 'scroll'); - animationFrameScheduler.flush(); -} - - -@Component({ - template: ` - -
      - {{i}} - {{item}} -
      -
      - `, - styles: [` - .cdk-virtual-scroll-content-wrapper { - display: flex; - flex-direction: column; - } - - .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper { - flex-direction: row; - } - `], - encapsulation: ViewEncapsulation.None -}) -class FixedSizeVirtualScroll { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; - // Casting virtualForOf as any so we can spy on private methods - @ViewChild(CdkVirtualForOf, {static: true}) virtualForOf: any; - - @Input() orientation = 'vertical'; - @Input() viewportSize = 200; - @Input() viewportCrossSize = 100; - @Input() itemSize = 50; - @Input() minBufferPx = 0; - @Input() maxBufferPx = 0; - @Input() items = Array(10).fill(0).map((_, i) => i); - @Input() trackBy: TrackByFunction; - @Input() templateCacheSize = 20; - - scrolledToIndex = 0; - - get viewportWidth() { - return this.orientation === 'horizontal' ? this.viewportSize : this.viewportCrossSize; - } - - get viewportHeight() { - return this.orientation === 'horizontal' ? this.viewportCrossSize : this.viewportSize; - } -} - -@Component({ - template: ` - -
      - {{i}} - {{item}} -
      -
      - `, - styles: [` - .cdk-virtual-scroll-content-wrapper { - display: flex; - flex-direction: column; - } - - .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper { - flex-direction: row; - } - `], - encapsulation: ViewEncapsulation.None -}) -class FixedSizeVirtualScrollWithRtlDirection { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; - - @Input() orientation = 'vertical'; - @Input() viewportSize = 200; - @Input() viewportCrossSize = 100; - @Input() itemSize = 50; - @Input() minBufferPx = 0; - @Input() maxBufferPx = 0; - @Input() items = Array(10).fill(0).map((_, i) => i); - @Input() trackBy: TrackByFunction; - @Input() templateCacheSize = 20; - - scrolledToIndex = 0; - - get viewportWidth() { - return this.orientation === 'horizontal' ? this.viewportSize : this.viewportCrossSize; - } - - get viewportHeight() { - return this.orientation === 'horizontal' ? this.viewportCrossSize : this.viewportSize; - } -} - -@Component({ - template: ` - -
      {{item}}
      -
      - ` -}) -class VirtualScrollWithNoStrategy { - items = []; -} diff --git a/packages/cdk/scrolling/virtual-scroll-viewport.ts b/packages/cdk/scrolling/virtual-scroll-viewport.ts deleted file mode 100644 index d308bf244..000000000 --- a/packages/cdk/scrolling/virtual-scroll-viewport.ts +++ /dev/null @@ -1,388 +0,0 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - Input, - NgZone, - OnDestroy, - OnInit, - Optional, - Output, - ViewChild, - ViewEncapsulation -} from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { ListRange } from '@ptsecurity/cdk/collections'; -import { animationFrameScheduler, Observable, Subject, Observer } from 'rxjs'; -import { auditTime, startWith, takeUntil } from 'rxjs/operators'; - -import { ScrollDispatcher } from './scroll-dispatcher'; -import { CdkScrollable, ExtendedScrollToOptions } from './scrollable'; -import { CdkVirtualForOf } from './virtual-for-of'; -import { VIRTUAL_SCROLL_STRATEGY, VirtualScrollStrategy } from './virtual-scroll-strategy'; - - -/** Checks if the given ranges are equal. */ -function rangesEqual(r1: ListRange, r2: ListRange): boolean { - return r1.start == r2.start && r1.end == r2.end; -} - - -/** A viewport that virtualizes it's scrolling with the help of `CdkVirtualForOf`. */ -@Component({ - selector: 'cdk-virtual-scroll-viewport', - templateUrl: 'virtual-scroll-viewport.html', - styleUrls: ['virtual-scroll-viewport.css'], - host: { - class: 'cdk-virtual-scroll-viewport', - '[class.cdk-virtual-scroll-orientation-horizontal]': 'orientation === "horizontal"', - '[class.cdk-virtual-scroll-orientation-vertical]': 'orientation !== "horizontal"' - }, - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [{ - provide: CdkScrollable, - useExisting: CdkVirtualScrollViewport - }] -}) -export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, OnDestroy { - - /** The direction the viewport scrolls. */ - @Input() orientation: 'horizontal' | 'vertical' = 'vertical'; - - // Note: we don't use the typical EventEmitter here because we need to subscribe to the scroll - // strategy lazily (i.e. only if the user is actually listening to the events). We do this because - // depending on how the strategy calculates the scrolled index, it may come at a cost to - // performance. - /** Emits when the index of the first element visible in the viewport changes. */ - @Output() scrolledIndexChange: Observable = - new Observable((observer: Observer) => - this._scrollStrategy.scrolledIndexChange.subscribe((index) => - Promise.resolve().then(() => this.ngZone.run(() => observer.next(index))))); - - /** The element that wraps the rendered content. */ - @ViewChild('contentWrapper', {static: true}) _contentWrapper: ElementRef; - - /** Emits when the viewport is detached from a CdkVirtualForOf. */ - private _detachedSubject = new Subject(); - - /** Emits when the rendered range changes. */ - private _renderedRangeSubject = new Subject(); - - /** A stream that emits whenever the rendered range changes. */ - renderedRangeStream: Observable = this._renderedRangeSubject.asObservable(); - - /** - * The transform used to scale the spacer to the same size as all content, including content that - * is not currently rendered. - */ - _totalContentSizeTransform = ''; - - /** - * The total size of all content (in pixels), including content that is not currently rendered. - */ - private _totalContentSize = 0; - - /** - * The CSS transform applied to the rendered subset of items so that they appear within the bounds - * of the visible viewport. - */ - private _renderedContentTransform: string; - - /** The currently rendered range of indices. */ - private _renderedRange: ListRange = {start: 0, end: 0}; - - /** The length of the data bound to this viewport (in number of items). */ - private _dataLength = 0; - - /** The size of the viewport (in pixels). */ - private _viewportSize = 0; - - /** the currently attached CdkVirtualForOf. */ - private _forOf: CdkVirtualForOf | null; - - /** The last rendered content offset that was set. */ - private _renderedContentOffset = 0; - - /** - * Whether the last rendered content offset was to the end of the content (and therefore needs to - * be rewritten as an offset to the start of the content). - */ - private _renderedContentOffsetNeedsRewrite = false; - - /** Whether there is a pending change detection cycle. */ - private _isChangeDetectionPending = false; - - /** A list of functions to run after the next change detection cycle. */ - private _runAfterChangeDetection: Function[] = []; - - constructor(public elementRef: ElementRef, - private _changeDetectorRef: ChangeDetectorRef, - ngZone: NgZone, - @Optional() @Inject(VIRTUAL_SCROLL_STRATEGY) - private _scrollStrategy: VirtualScrollStrategy, - @Optional() dir: Directionality, - scrollDispatcher: ScrollDispatcher) { - super(elementRef, scrollDispatcher, ngZone, dir); - - if (!_scrollStrategy) { - throw Error('Error: cdk-virtual-scroll-viewport requires the "itemSize" property to be set.'); - } - } - - ngOnInit() { - super.ngOnInit(); - - // It's still too early to measure the viewport at this point. Deferring with a promise allows - // the Viewport to be rendered with the correct size before we measure. We run this outside the - // zone to avoid causing more change detection cycles. We handle the change detection loop - // ourselves instead. - this.ngZone.runOutsideAngular(() => Promise.resolve().then(() => { - this._measureViewportSize(); - this._scrollStrategy.attach(this); - - this.elementScrolled() - .pipe( - // Start off with a fake scroll event so we properly detect our initial position. - startWith(null!), - // Collect multiple events into one until the next animation frame. This way if - // there are multiple scroll events in the same frame we only need to recheck - // our layout once. - auditTime(0, animationFrameScheduler)) - .subscribe(() => this._scrollStrategy.onContentScrolled()); - - this._markChangeDetectionNeeded(); - })); - } - - ngOnDestroy() { - this.detach(); - this._scrollStrategy.detach(); - - // Complete all subjects - this._renderedRangeSubject.complete(); - this._detachedSubject.complete(); - - super.ngOnDestroy(); - } - - /** Attaches a `CdkVirtualForOf` to this viewport. */ - attach(forOf: CdkVirtualForOf) { - if (this._forOf) { - throw Error('CdkVirtualScrollViewport is already attached.'); - } - - // Subscribe to the data stream of the CdkVirtualForOf to keep track of when the data length - // changes. Run outside the zone to avoid triggering change detection, since we're managing the - // change detection loop ourselves. - this.ngZone.runOutsideAngular(() => { - this._forOf = forOf; - this._forOf.dataStream.pipe(takeUntil(this._detachedSubject)).subscribe((data) => { - const newLength = data.length; - if (newLength !== this._dataLength) { - this._dataLength = newLength; - this._scrollStrategy.onDataLengthChanged(); - } - this._doChangeDetection(); - }); - }); - } - - /** Detaches the current `CdkVirtualForOf`. */ - detach() { - this._forOf = null; - this._detachedSubject.next(); - } - - /** Gets the length of the data bound to this viewport (in number of items). */ - getDataLength(): number { - return this._dataLength; - } - - /** Gets the size of the viewport (in pixels). */ - getViewportSize(): number { - return this._viewportSize; - } - - // TODO(mmalerba): This is technically out of sync with what's really rendered until a render - // cycle happens. I'm being careful to only call it after the render cycle is complete and before - // setting it to something else, but its error prone and should probably be split into - // `pendingRange` and `renderedRange`, the latter reflecting whats actually in the DOM. - - /** Get the current rendered range of items. */ - getRenderedRange(): ListRange { - return this._renderedRange; - } - - /** - * Sets the total size of all content (in pixels), including content that is not currently - * rendered. - */ - setTotalContentSize(size: number) { - if (this._totalContentSize !== size) { - this._totalContentSize = size; - const axis = this.orientation == 'horizontal' ? 'X' : 'Y'; - this._totalContentSizeTransform = `scale${axis}(${this._totalContentSize})`; - this._markChangeDetectionNeeded(); - } - } - - /** Sets the currently rendered range of indices. */ - setRenderedRange(range: ListRange) { - if (!rangesEqual(this._renderedRange, range)) { - this._renderedRangeSubject.next(this._renderedRange = range); - this._markChangeDetectionNeeded(() => this._scrollStrategy.onContentRendered()); - } - } - - /** - * Gets the offset from the start of the viewport to the start of the rendered data (in pixels). - */ - getOffsetToRenderedContentStart(): number | null { - return this._renderedContentOffsetNeedsRewrite ? null : this._renderedContentOffset; - } - - /** - * Sets the offset from the start of the viewport to either the start or end of the rendered data - * (in pixels). - */ - setRenderedContentOffset(offset: number, to: 'to-start' | 'to-end' = 'to-start') { - // For a horizontal viewport in a right-to-left language we need to translate along the x-axis - // in the negative direction. - const isRtl = this.dir && this.dir.value == 'rtl'; - const isHorizontal = this.orientation == 'horizontal'; - const axis = isHorizontal ? 'X' : 'Y'; - const axisDirection = isHorizontal && isRtl ? -1 : 1; - let transform = `translate${axis}(${Number(axisDirection * offset)}px)`; - this._renderedContentOffset = offset; - if (to === 'to-end') { - transform += ` translate${axis}(-100%)`; - // The viewport should rewrite this as a `to-start` offset on the next render cycle. Otherwise - // elements will appear to expand in the wrong direction (e.g. `mc-expansion-panel` would - // expand upward). - this._renderedContentOffsetNeedsRewrite = true; - } - if (this._renderedContentTransform != transform) { - // We know this value is safe because we parse `offset` with `Number()` before passing it - // into the string. - this._renderedContentTransform = transform; - this._markChangeDetectionNeeded(() => { - if (this._renderedContentOffsetNeedsRewrite) { - this._renderedContentOffset -= this.measureRenderedContentSize(); - this._renderedContentOffsetNeedsRewrite = false; - this.setRenderedContentOffset(this._renderedContentOffset); - } else { - this._scrollStrategy.onRenderedOffsetChanged(); - } - }); - } - } - - /** - * Scrolls to the given offset from the start of the viewport. Please note that this is not always - * the same as setting `scrollTop` or `scrollLeft`. In a horizontal viewport with right-to-left - * direction, this would be the equivalent of setting a fictional `scrollRight` property. - * @param offset The offset to scroll to. - * @param behavior The ScrollBehavior to use when scrolling. Default is behavior is `auto`. - */ - scrollToOffset(offset: number, behavior: ScrollBehavior = 'auto') { - const options: ExtendedScrollToOptions = {behavior}; - if (this.orientation === 'horizontal') { - options.start = offset; - } else { - options.top = offset; - } - this.scrollTo(options); - } - - /** - * Scrolls to the offset for the given index. - * @param index The index of the element to scroll to. - * @param behavior The ScrollBehavior to use when scrolling. Default is behavior is `auto`. - */ - scrollToIndex(index: number, behavior: ScrollBehavior = 'auto') { - this._scrollStrategy.scrollToIndex(index, behavior); - } - - /** - * Gets the current scroll offset from the start of the viewport (in pixels). - * @param from The edge to measure the offset from. Defaults to 'top' in vertical mode and 'start' - * in horizontal mode. - */ - measureScrollOffset(from?: 'top' | 'left' | 'right' | 'bottom' | 'start' | 'end'): number { - return super.measureScrollOffset( - from ? from : this.orientation === 'horizontal' ? 'start' : 'top'); - } - - /** Measure the combined size of all of the rendered items. */ - measureRenderedContentSize(): number { - const contentEl = this._contentWrapper.nativeElement; - - return this.orientation === 'horizontal' ? contentEl.offsetWidth : contentEl.offsetHeight; - } - - /** - * Measure the total combined size of the given range. Throws if the range includes items that are - * not rendered. - */ - measureRangeSize(range: ListRange): number { - if (!this._forOf) { - return 0; - } - - return this._forOf.measureRangeSize(range, this.orientation); - } - - /** Update the viewport dimensions and re-render. */ - checkViewportSize() { - // TODO: Cleanup later when add logic for handling content resize - this._measureViewportSize(); - this._scrollStrategy.onDataLengthChanged(); - } - - /** Measure the viewport size. */ - private _measureViewportSize() { - const viewportEl = this.elementRef.nativeElement; - this._viewportSize = this.orientation === 'horizontal' ? - viewportEl.clientWidth : viewportEl.clientHeight; - } - - /** Queue up change detection to run. */ - private _markChangeDetectionNeeded(runAfter?: Function) { - if (runAfter) { - this._runAfterChangeDetection.push(runAfter); - } - - // Use a Promise to batch together calls to `_doChangeDetection`. This way if we set a bunch of - // properties sequentially we only have to run `_doChangeDetection` once at the end. - if (!this._isChangeDetectionPending) { - this._isChangeDetectionPending = true; - this.ngZone.runOutsideAngular(() => Promise.resolve().then(() => { - this._doChangeDetection(); - })); - } - } - - /** Run change detection. */ - private _doChangeDetection() { - this._isChangeDetectionPending = false; - - // Apply changes to Angular bindings. Note: We must call `markForCheck` to run change detection - // from the root, since the repeated items are content projected in. Calling `detectChanges` - // instead does not properly check the projected content. - this.ngZone.run(() => this._changeDetectorRef.markForCheck()); - // Apply the content transform. The transform can't be set via an Angular binding because - // bypassSecurityTrustStyle is banned in Google. However the value is safe, it's composed of - // string literals, a variable that can only be 'X' or 'Y', and user input that is run through - // the `Number` function first to coerce it to a numeric value. - this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform; - - const runAfterChangeDetection = this._runAfterChangeDetection; - this._runAfterChangeDetection = []; - for (const fn of runAfterChangeDetection) { - fn(); - } - } -} diff --git a/packages/cdk/style-manager/index.ts b/packages/cdk/style-manager/index.ts deleted file mode 100644 index 2530908a6..000000000 --- a/packages/cdk/style-manager/index.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export * from './public-api'; diff --git a/packages/cdk/style-manager/public-api.ts b/packages/cdk/style-manager/public-api.ts deleted file mode 100644 index fa549bdfc..000000000 --- a/packages/cdk/style-manager/public-api.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export * from './style-manager'; diff --git a/packages/cdk/style-manager/style-manager.spec.ts b/packages/cdk/style-manager/style-manager.spec.ts deleted file mode 100644 index 8e39a611f..000000000 --- a/packages/cdk/style-manager/style-manager.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { HttpClientModule } from '@angular/common/http'; -import { inject, TestBed } from '@angular/core/testing'; - -import { StyleManager } from './style-manager'; - - -xdescribe('StyleManager', () => { - let styleManager: StyleManager; - - beforeEach(() => TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [StyleManager] - })); - - beforeEach(inject([StyleManager], (sm: StyleManager) => { - styleManager = sm; - })); - - afterEach(() => { - const links = document.head!.querySelectorAll('link'); - - for (const link of Array.prototype.slice.call(links)) { - if (link.className.includes('style-manager-')) { - document.head!.removeChild(link); - } - } - }); - - it('should add stylesheet to head', () => { - styleManager.setStyle('test', 'test.css'); - const styleEl = document.head!.querySelector('.style-manager-test') as HTMLLinkElement; - expect(styleEl).not.toBeNull(); - expect(styleEl.href.endsWith('test.css')).toBe(true); - }); - - it('should remove existing stylesheet', () => { - styleManager.setStyle('test', 'test.css'); - let styleEl = document.head!.querySelector('.style-manager-test') as HTMLLinkElement; - expect(styleEl).not.toBeNull(); - expect(styleEl.href.endsWith('test.css')).toBe(true); - - styleManager.removeStyle('test'); - styleEl = document.head!.querySelector('.style-manager-test') as HTMLLinkElement; - expect(styleEl).toBeNull(); - }); -}); diff --git a/packages/cdk/style-manager/style-manager.ts b/packages/cdk/style-manager/style-manager.ts deleted file mode 100644 index 89ab331f0..000000000 --- a/packages/cdk/style-manager/style-manager.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Injectable } from '@angular/core'; - - -/** - * Class for managing stylesheets. Stylesheets are loaded into named slots so that they can be - * removed or changed later. - */ -@Injectable({providedIn: 'root'}) -export class StyleManager { - /** - * Set the stylesheet with the specified key. - */ - setStyle(key: string, href: string) { - getLinkElementForKey(key).setAttribute('href', href); - } - - /** - * Remove the stylesheet with the specified key. - */ - removeStyle(key: string) { - const existingLinkElement = getExistingLinkElementByKey(key); - if (existingLinkElement) { - document.head!.removeChild(existingLinkElement); - } - } -} - -function getLinkElementForKey(key: string) { - return getExistingLinkElementByKey(key) || createLinkElementWithKey(key); -} - -function getExistingLinkElementByKey(key: string) { - return document.head!.querySelector(`link[rel="stylesheet"].${getClassNameForKey(key)}`); -} - -function createLinkElementWithKey(key: string) { - const linkEl = document.createElement('link'); - linkEl.setAttribute('rel', 'stylesheet'); - linkEl.classList.add(getClassNameForKey(key)); - document.head!.appendChild(linkEl); - - return linkEl; -} - -function getClassNameForKey(key: string) { - return `style-manager-${key}`; -} diff --git a/packages/cdk/style-manager/tsconfig.build.json b/packages/cdk/style-manager/tsconfig.build.json deleted file mode 100644 index c9ee29c59..000000000 --- a/packages/cdk/style-manager/tsconfig.build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.build", - "files": [ - "public-api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@ptsecurity/cdk/style-manager", - "skipTemplateCodegen": true, - "fullTemplateTypeCheck": true - } -} diff --git a/packages/cdk/tree/control/base-tree-control.ts b/packages/cdk/tree/control/base-tree-control.ts index b02bcf7d3..4bae25bef 100644 --- a/packages/cdk/tree/control/base-tree-control.ts +++ b/packages/cdk/tree/control/base-tree-control.ts @@ -1,4 +1,4 @@ -import { SelectionModel } from '@ptsecurity/cdk/collections'; +import { SelectionModel } from '@angular/cdk/collections'; import { Observable } from 'rxjs'; import { ITreeControl } from './tree-control'; diff --git a/packages/cdk/tree/control/tree-control.ts b/packages/cdk/tree/control/tree-control.ts index 8864713d5..b2b109238 100644 --- a/packages/cdk/tree/control/tree-control.ts +++ b/packages/cdk/tree/control/tree-control.ts @@ -1,5 +1,4 @@ -import { SelectionModel } from '@ptsecurity/cdk/collections'; - +import { SelectionModel } from '@angular/cdk/collections'; import { Observable } from 'rxjs'; diff --git a/packages/cdk/tree/padding.ts b/packages/cdk/tree/padding.ts index 3448e1690..44e9681a6 100644 --- a/packages/cdk/tree/padding.ts +++ b/packages/cdk/tree/padding.ts @@ -1,7 +1,5 @@ +import { Directionality } from '@angular/cdk/bidi'; import { Directive, ElementRef, Input, OnDestroy, Optional, Renderer2 } from '@angular/core'; - -import { Directionality } from '@ptsecurity/cdk/bidi'; - import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; diff --git a/packages/cdk/tree/tree._spec.ts b/packages/cdk/tree/tree._spec.ts index 2fe3fd87d..9dd38b857 100644 --- a/packages/cdk/tree/tree._spec.ts +++ b/packages/cdk/tree/tree._spec.ts @@ -1,8 +1,6 @@ -import { ComponentFixture, TestBed, fakeAsync, flush } from '@angular/core/testing'; - +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { Component, ViewChild, TrackByFunction } from '@angular/core'; - -import { ICollectionViewer, DataSource } from '@ptsecurity/cdk/collections'; +import { ComponentFixture, TestBed, fakeAsync, flush } from '@angular/core/testing'; import { combineLatest, BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -906,7 +904,7 @@ class FakeDataSource extends DataSource { } } - connect(collectionViewer: ICollectionViewer): Observable { + connect(collectionViewer: CollectionViewer): Observable { this.isConnected = true; const streams = [this._dataChange, collectionViewer.viewChange]; return combineLatest(streams) diff --git a/packages/cdk/tree/tree.ts b/packages/cdk/tree/tree.ts index 4559843db..d73814873 100644 --- a/packages/cdk/tree/tree.ts +++ b/packages/cdk/tree/tree.ts @@ -1,3 +1,4 @@ +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { AfterContentChecked, ChangeDetectionStrategy, @@ -18,10 +19,7 @@ import { ViewEncapsulation, TrackByFunction, Inject, forwardRef } from '@angular/core'; - import { IFocusableOption } from '@ptsecurity/cdk/a11y'; -import { ICollectionViewer, DataSource } from '@ptsecurity/cdk/collections'; - import { BehaviorSubject, Observable, of as observableOf, Subject, Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -52,7 +50,7 @@ import { encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }) -export class CdkTree implements AfterContentChecked, ICollectionViewer, OnDestroy, OnInit { +export class CdkTree implements AfterContentChecked, CollectionViewer, OnDestroy, OnInit { /** The tree controller */ @Input() treeControl: ITreeControl; @@ -154,7 +152,7 @@ export class CdkTree implements AfterContentChecked, ICollectionViewer, OnDes /** Check for changes made in the data and render each change (node added/removed/moved). */ renderNodeChanges( - data: T[], + data: T[] | ReadonlyArray, dataDiffer: IterableDiffer = this.dataDiffer, viewContainer: ViewContainerRef = this.nodeOutlet.viewContainer, parentData?: T @@ -233,7 +231,7 @@ export class CdkTree implements AfterContentChecked, ICollectionViewer, OnDes /** Set up a subscription for the data provided by the data source. */ private observeRenderChanges() { - let dataStream: Observable | undefined; + let dataStream: Observable> | undefined; // Cannot use `instanceof DataSource` since the data source could be a literal with // `connect` function and may not extends DataSource. diff --git a/packages/dev-app/dev-app-module.ts b/packages/dev-app/dev-app-module.ts index 1c8b9bc7c..cb45dea4b 100644 --- a/packages/dev-app/dev-app-module.ts +++ b/packages/dev-app/dev-app-module.ts @@ -5,7 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterModule } from '@angular/router'; -import { LayoutModule } from '@ptsecurity/cdk/layout'; +import { LayoutModule } from '@angular/cdk/layout'; import { ButtonDemo } from './button/button-demo'; import { DatepickerDemo } from './datepicker/datepicker-demo'; diff --git a/packages/dev-app/mosaic-module.ts b/packages/dev-app/mosaic-module.ts index fbc468301..604d910c6 100644 --- a/packages/dev-app/mosaic-module.ts +++ b/packages/dev-app/mosaic-module.ts @@ -1,9 +1,9 @@ /** * NgModule that includes all Mosaic modules that are required to serve the dev-app. */ +import { BidiModule } from '@angular/cdk/bidi'; import { NgModule } from '@angular/core'; import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { BidiModule } from '@ptsecurity/cdk/bidi'; import { McButtonModule, McIconModule, diff --git a/packages/dev-app/system-config.ts b/packages/dev-app/system-config.ts index ac2994f75..774e47711 100644 --- a/packages/dev-app/system-config.ts +++ b/packages/dev-app/system-config.ts @@ -15,6 +15,18 @@ System.config({ 'rxjs': 'node_modules/rxjs/bundles/rxjs.umd.min.js', 'rxjs/operators': 'system-rxjs-operators.js', + // Angular CDK specific mappings. + '@angular/cdk/a11y': 'node:@angular/cdk/bundles/cdk-a11y.umd.js', + '@angular/cdk/bidi': 'node:@angular/cdk/bundles/cdk-bidi.umd.js', + '@angular/cdk/coercion': 'node:@angular/cdk/bundles/cdk-coercion.umd.js', + '@angular/cdk/collections': 'node:@angular/cdk/bundles/cdk-collections.umd.js', + '@angular/cdk/keycodes': 'node:@angular/cdk/bundles/cdk-keycodes.umd.js', + '@angular/cdk/platform': 'node:@angular/cdk/bundles/cdk-platform.umd.js', + '@angular/cdk/portal': 'node:@angular/cdk/bundles/cdk-portal.umd.js', + '@angular/cdk/overlay': 'node:@angular/cdk/bundles/cdk-overlay.umd.js', + '@angular/cdk/observers': 'node:@angular/cdk/bundles/cdk-observers.umd.js', + '@angular/cdk/scrolling': 'node:@angular/cdk/bundles/cdk-scrolling.umd.js', + // Angular specific mappings. '@angular/core': 'node:@angular/core/bundles/core.umd.js', '@angular/core/testing': 'node:@angular/core/bundles/core-testing.umd.js', @@ -42,16 +54,8 @@ System.config({ '@ptsecurity/cdk': 'dist/packages/cdk/index.js', '@ptsecurity/cdk/a11y': 'dist/packages/cdk/a11y/index.js', - '@ptsecurity/cdk/bidi': 'dist/packages/cdk/bidi/index.js', '@ptsecurity/cdk/datetime': 'dist/packages/cdk/datetime/index.js', - '@ptsecurity/cdk/coercion': 'dist/packages/cdk/coercion/index.js', - '@ptsecurity/cdk/collections': 'dist/packages/cdk/collections/index.js', '@ptsecurity/cdk/keycodes': 'dist/packages/cdk/keycodes/index.js', - '@ptsecurity/cdk/layout': 'dist/packages/cdk/layout/index.js', - '@ptsecurity/cdk/overlay': 'dist/packages/cdk/overlay/index.js', - '@ptsecurity/cdk/platform': 'dist/packages/cdk/platform/index.js', - '@ptsecurity/cdk/portal': 'dist/packages/cdk/portal/index.js', - '@ptsecurity/cdk/scrolling': 'dist/packages/cdk/scrolling/index.js', '@ptsecurity/cdk/testing': 'dist/packages/cdk/testing/index.js', '@ptsecurity/cdk/tree': 'dist/packages/cdk/tree/index.js', diff --git a/packages/mosaic-dev/cdk-virtual-scroll-custom-strategy/module.ts b/packages/mosaic-dev/cdk-virtual-scroll-custom-strategy/module.ts index b436f16f3..ea0604b16 100644 --- a/packages/mosaic-dev/cdk-virtual-scroll-custom-strategy/module.ts +++ b/packages/mosaic-dev/cdk-virtual-scroll-custom-strategy/module.ts @@ -1,10 +1,8 @@ +import { ScrollingModule, VIRTUAL_SCROLL_STRATEGY, FixedSizeVirtualScrollStrategy } from '@angular/cdk/scrolling'; import { ChangeDetectionStrategy, Component, NgModule, ViewEncapsulation } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { ScrollingModule, VIRTUAL_SCROLL_STRATEGY } from '../../cdk/scrolling'; -import { FixedSizeVirtualScrollStrategy } from '../../cdk/scrolling/fixed-size-virtual-scroll'; - export class CustomVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy { constructor() { diff --git a/packages/mosaic-dev/cdk-virtual-scroll-data-source/module.ts b/packages/mosaic-dev/cdk-virtual-scroll-data-source/module.ts index 96c7a2814..5e73df72c 100644 --- a/packages/mosaic-dev/cdk-virtual-scroll-data-source/module.ts +++ b/packages/mosaic-dev/cdk-virtual-scroll-data-source/module.ts @@ -1,11 +1,10 @@ +import { DataSource, CollectionViewer } from '@angular/cdk/collections'; +import { ScrollingModule } from '@angular/cdk/scrolling'; import { ChangeDetectionStrategy, Component, NgModule, ViewEncapsulation } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; -import { DataSource, ICollectionViewer } from '../../cdk/collections'; -import { ScrollingModule } from '../../cdk/scrolling'; - export class MyDataSource extends DataSource { private length = 100000; @@ -15,7 +14,7 @@ export class MyDataSource extends DataSource { private dataStream = new BehaviorSubject<(string | undefined)[]>(this.cachedData); private subscription = new Subscription(); - connect(collectionViewer: ICollectionViewer): Observable<(string | undefined)[]> { + connect(collectionViewer: CollectionViewer): Observable<(string | undefined)[]> { this.subscription.add(collectionViewer.viewChange.subscribe((range) => { const startPage = this.getPageForIndex(range.start); const endPage = this.getPageForIndex(range.end - 1); diff --git a/packages/mosaic-dev/tree/module.ts b/packages/mosaic-dev/tree/module.ts index 889a79f31..3ed8725f9 100644 --- a/packages/mosaic-dev/tree/module.ts +++ b/packages/mosaic-dev/tree/module.ts @@ -2,16 +2,14 @@ import { Component, Injectable, NgModule, ViewEncapsulation } from '@angular/cor import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - import { FlatTreeControl, NestedTreeControl } from '@ptsecurity/cdk/tree'; +import { McIconModule } from '@ptsecurity/mosaic/icon'; import { McTreeFlatDataSource, McTreeFlattener, McTreeNestedDataSource, McTreeModule } from '@ptsecurity/mosaic/tree'; - -import { McIconModule } from '@ptsecurity/mosaic/icon'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; diff --git a/packages/mosaic-moment-adapter/package.json b/packages/mosaic-moment-adapter/package.json index e3ca986d4..468fa2481 100644 --- a/packages/mosaic-moment-adapter/package.json +++ b/packages/mosaic-moment-adapter/package.json @@ -17,6 +17,7 @@ "homepage": "https://github.com/positive-js/mosaic#readme", "peerDependencies": { "@angular/core": "0.0.0-NG", + "@angular/cdk": "0.0.0-NG", "@ptsecurity/cdk": "0.0.0-PLACEHOLDER", "messageformat": "^2.0.5", "moment": "^2.24.0" diff --git a/packages/mosaic/autocomplete/autocomplete-trigger.directive.ts b/packages/mosaic/autocomplete/autocomplete-trigger.directive.ts index 196369dbc..c6111e358 100644 --- a/packages/mosaic/autocomplete/autocomplete-trigger.directive.ts +++ b/packages/mosaic/autocomplete/autocomplete-trigger.directive.ts @@ -1,3 +1,5 @@ +import { Directionality } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DOCUMENT } from '@angular/common'; import { ChangeDetectorRef, @@ -14,19 +16,17 @@ import { ViewContainerRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { DOWN_ARROW, ENTER, ESCAPE, TAB, UP_ARROW } from '@ptsecurity/cdk/keycodes'; import { FlexibleConnectedPositionStrategy, Overlay, OverlayConfig, OverlayRef, - IPositionStrategy, - IScrollStrategy, IConnectedPosition -} from '@ptsecurity/cdk/overlay'; -import { TemplatePortal } from '@ptsecurity/cdk/portal'; -import { ViewportRuler } from '@ptsecurity/cdk/scrolling'; + PositionStrategy, + ScrollStrategy, ConnectedPosition +} from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { ViewportRuler } from '@angular/cdk/scrolling'; import { countGroupLabelsBeforeOption, getOptionScrollPosition, @@ -57,10 +57,10 @@ export const AUTOCOMPLETE_BORDER_WIDTH: number = 2; /** Injection token that determines the scroll handling while the autocomplete panel is open. */ export const MC_AUTOCOMPLETE_SCROLL_STRATEGY = - new InjectionToken<() => IScrollStrategy>('mc-autocomplete-scroll-strategy'); + new InjectionToken<() => ScrollStrategy>('mc-autocomplete-scroll-strategy'); // tslint:disable-next-line naming-convention -export function MC_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY(overlay: Overlay): () => IScrollStrategy { +export function MC_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY(overlay: Overlay): () => ScrollStrategy { return () => overlay.scrollStrategies.reposition(); } @@ -172,7 +172,7 @@ export class McAutocompleteTrigger implements ControlValueAccessor, OnDestroy { private componentDestroyed = false; - private scrollStrategy: () => IScrollStrategy; + private scrollStrategy: () => ScrollStrategy; /** Old value of the native input. Used to work around issues with the `input` event on IE. */ private previousValue: string | number | null; @@ -611,7 +611,7 @@ export class McAutocompleteTrigger implements ControlValueAccessor, OnDestroy { }); } - private getOverlayPosition(): IPositionStrategy { + private getOverlayPosition(): PositionStrategy { this.positionStrategy = this.overlay.position() .flexibleConnectedTo(this.getConnectedElement()) .withFlexibleDimensions(false) @@ -634,7 +634,7 @@ export class McAutocompleteTrigger implements ControlValueAccessor, OnDestroy { // border-radius based on the overlay position. panelClass: 'mc-autocomplete-panel-above' } - ] as IConnectedPosition[]); + ] as ConnectedPosition[]); return this.positionStrategy; } diff --git a/packages/mosaic/autocomplete/autocomplete.component.ts b/packages/mosaic/autocomplete/autocomplete.component.ts index 36a67db6a..edbd3701c 100644 --- a/packages/mosaic/autocomplete/autocomplete.component.ts +++ b/packages/mosaic/autocomplete/autocomplete.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { AfterContentInit, ChangeDetectionStrategy, @@ -16,7 +17,6 @@ import { ViewEncapsulation } from '@angular/core'; import { ActiveDescendantKeyManager } from '@ptsecurity/cdk/a11y'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { MC_OPTION_PARENT_COMPONENT, McOptgroup, McOption } from '@ptsecurity/mosaic/core'; diff --git a/packages/mosaic/autocomplete/autocomplete.module.ts b/packages/mosaic/autocomplete/autocomplete.module.ts index c4eeacb8c..4a38dacd9 100644 --- a/packages/mosaic/autocomplete/autocomplete.module.ts +++ b/packages/mosaic/autocomplete/autocomplete.module.ts @@ -1,6 +1,6 @@ +import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; import { McOptionModule, McCommonModule } from '@ptsecurity/mosaic/core'; import { McAutocompleteOrigin } from './autocomplete-origin.directive'; diff --git a/packages/mosaic/autocomplete/autocomplete.spec.ts b/packages/mosaic/autocomplete/autocomplete.spec.ts index 99ffcb7d4..c308b55f8 100644 --- a/packages/mosaic/autocomplete/autocomplete.spec.ts +++ b/packages/mosaic/autocomplete/autocomplete.spec.ts @@ -28,10 +28,10 @@ import { import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Directionality } from '@ptsecurity/cdk/bidi'; +import { Directionality } from '@angular/cdk/bidi'; import { DOWN_ARROW, ENTER, ESCAPE, SPACE, TAB, UP_ARROW } from '@ptsecurity/cdk/keycodes'; -import { Overlay, OverlayContainer } from '@ptsecurity/cdk/overlay'; -import { ScrollDispatcher } from '@ptsecurity/cdk/scrolling'; +import { Overlay, OverlayContainer } from '@angular/cdk/overlay'; +import { ScrollDispatcher } from '@angular/cdk/scrolling'; import { createKeyboardEvent, dispatchFakeEvent, diff --git a/packages/mosaic/button-toggle/button-toggle.component.ts b/packages/mosaic/button-toggle/button-toggle.component.ts index c48119522..a6c42645a 100644 --- a/packages/mosaic/button-toggle/button-toggle.component.ts +++ b/packages/mosaic/button-toggle/button-toggle.component.ts @@ -1,3 +1,5 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { SelectionModel } from '@angular/cdk/collections'; import { AfterContentInit, ChangeDetectionStrategy, @@ -19,8 +21,6 @@ import { } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FocusMonitor } from '@ptsecurity/cdk/a11y'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; -import { SelectionModel } from '@ptsecurity/cdk/collections'; import { McButton } from '@ptsecurity/mosaic/button'; diff --git a/packages/mosaic/button/button.module.ts b/packages/mosaic/button/button.module.ts index 71f4860d3..d63a866cc 100644 --- a/packages/mosaic/button/button.module.ts +++ b/packages/mosaic/button/button.module.ts @@ -1,7 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { McButton, diff --git a/packages/mosaic/card/card.module.ts b/packages/mosaic/card/card.module.ts index 0bdac7bc6..a302c4cd0 100644 --- a/packages/mosaic/card/card.module.ts +++ b/packages/mosaic/card/card.module.ts @@ -1,8 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; - import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { McCard diff --git a/packages/mosaic/core/_core.scss b/packages/mosaic/core/_core.scss index 20f53923c..38d3d1367 100644 --- a/packages/mosaic/core/_core.scss +++ b/packages/mosaic/core/_core.scss @@ -1,7 +1,7 @@ @import 'styles/common/animation'; @import '../../cdk/a11y/a11y'; -@import '../../cdk/overlay/overlay'; +@import '../../../node_modules/@angular/cdk/overlay'; @import './selection/pseudo-checkbox/pseudo-checkbox-theme'; diff --git a/packages/mosaic/core/common-behaviors/common-module.ts b/packages/mosaic/core/common-behaviors/common-module.ts index 61bc90de9..9197d63ca 100644 --- a/packages/mosaic/core/common-behaviors/common-module.ts +++ b/packages/mosaic/core/common-behaviors/common-module.ts @@ -1,5 +1,5 @@ +import { BidiModule } from '@angular/cdk/bidi'; import { NgModule, InjectionToken, Optional, Inject, isDevMode } from '@angular/core'; -import { BidiModule } from '@ptsecurity/cdk/bidi'; // Injection token that configures whether the Mosaic sanity checks are enabled. diff --git a/packages/mosaic/core/common-behaviors/disabled.ts b/packages/mosaic/core/common-behaviors/disabled.ts index 2306642b1..9e252e4e6 100644 --- a/packages/mosaic/core/common-behaviors/disabled.ts +++ b/packages/mosaic/core/common-behaviors/disabled.ts @@ -1,4 +1,4 @@ -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Constructor } from './constructor'; diff --git a/packages/mosaic/core/option/option.ts b/packages/mosaic/core/option/option.ts index 94e8ddcac..c5a7c4d6b 100644 --- a/packages/mosaic/core/option/option.ts +++ b/packages/mosaic/core/option/option.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { AfterViewChecked, ChangeDetectionStrategy, @@ -14,7 +15,6 @@ import { QueryList, ViewEncapsulation } from '@angular/core'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { ENTER, SPACE } from '@ptsecurity/cdk/keycodes'; import { Subject } from 'rxjs'; diff --git a/packages/mosaic/core/overlay/overlay-position-map.ts b/packages/mosaic/core/overlay/overlay-position-map.ts index d43275b67..37ee58ffb 100644 --- a/packages/mosaic/core/overlay/overlay-position-map.ts +++ b/packages/mosaic/core/overlay/overlay-position-map.ts @@ -1,4 +1,4 @@ -import { ConnectionPositionPair } from '@ptsecurity/cdk/overlay'; +import { ConnectionPositionPair } from '@angular/cdk/overlay'; export const POSITION_MAP: { [key: string]: ConnectionPositionPair } = { diff --git a/packages/mosaic/core/select/constants.ts b/packages/mosaic/core/select/constants.ts index e3bd63ce4..797ba6486 100644 --- a/packages/mosaic/core/select/constants.ts +++ b/packages/mosaic/core/select/constants.ts @@ -1,5 +1,5 @@ import { InjectionToken } from '@angular/core'; -import { IScrollStrategy, Overlay, RepositionScrollStrategy } from '@ptsecurity/cdk/overlay'; +import { ScrollStrategy, Overlay, RepositionScrollStrategy } from '@angular/cdk/overlay'; /** The max height of the select's overlay panel */ @@ -21,7 +21,7 @@ export const SELECT_PANEL_VIEWPORT_PADDING = 8; /** Injection token that determines the scroll handling while a select is open. */ export const MC_SELECT_SCROLL_STRATEGY = - new InjectionToken<() => IScrollStrategy>('mc-select-scroll-strategy'); + new InjectionToken<() => ScrollStrategy>('mc-select-scroll-strategy'); /** @docs-private */ export function mcSelectScrollStrategyProviderFactory(overlay: Overlay): diff --git a/packages/mosaic/datepicker/calendar-header.spec.ts b/packages/mosaic/datepicker/calendar-header.spec.ts index 02afd1224..8eb198227 100644 --- a/packages/mosaic/datepicker/calendar-header.spec.ts +++ b/packages/mosaic/datepicker/calendar-header.spec.ts @@ -1,8 +1,8 @@ // tslint:disable:no-magic-numbers +import { Directionality } from '@angular/cdk/bidi'; import { Component } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { McMomentDateModule } from '@ptsecurity/mosaic-moment-adapter/adapter'; import { McCalendar } from './calendar'; diff --git a/packages/mosaic/datepicker/calendar.spec.ts b/packages/mosaic/datepicker/calendar.spec.ts index ae46ccfba..553cd978a 100644 --- a/packages/mosaic/datepicker/calendar.spec.ts +++ b/packages/mosaic/datepicker/calendar.spec.ts @@ -1,9 +1,9 @@ // tslint:disable:no-magic-numbers // tslint:disable:no-unbound-method +import { Directionality } from '@angular/cdk/bidi'; import { Component, NgZone } from '@angular/core'; import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { DateAdapter } from '@ptsecurity/cdk/datetime'; import { ENTER, RIGHT_ARROW, SPACE } from '@ptsecurity/cdk/keycodes'; import { diff --git a/packages/mosaic/datepicker/calendar.ts b/packages/mosaic/datepicker/calendar.ts index 3f711e7e9..278cb9d94 100644 --- a/packages/mosaic/datepicker/calendar.ts +++ b/packages/mosaic/datepicker/calendar.ts @@ -1,3 +1,4 @@ +import { ComponentPortal, ComponentType, Portal } from '@angular/cdk/portal'; import { AfterContentInit, AfterViewChecked, @@ -17,7 +18,6 @@ import { ViewEncapsulation } from '@angular/core'; import { DateAdapter, MC_DATE_FORMATS, McDateFormats } from '@ptsecurity/cdk/datetime'; -import { ComponentPortal, IComponentType, Portal } from '@ptsecurity/cdk/portal'; import { Subject, Subscription } from 'rxjs'; import { McCalendarCellCssClasses } from './calendar-body'; @@ -236,7 +236,7 @@ export class McCalendar implements AfterContentInit, AfterViewChecked, OnDest } /** An input indicating the type of the header component, if set. */ - @Input() headerComponent: IComponentType; + @Input() headerComponent: ComponentType; /** A portal containing the header component type for this calendar. */ calendarHeaderPortal: Portal; diff --git a/packages/mosaic/datepicker/datepicker-input.ts b/packages/mosaic/datepicker/datepicker-input.ts index 7aeca9b4b..95043cfed 100644 --- a/packages/mosaic/datepicker/datepicker-input.ts +++ b/packages/mosaic/datepicker/datepicker-input.ts @@ -1,4 +1,5 @@ // tslint:disable:no-empty +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Directive, ElementRef, @@ -20,7 +21,6 @@ import { ValidatorFn, Validators } from '@angular/forms'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { DateAdapter, MC_DATE_FORMATS, McDateFormats } from '@ptsecurity/cdk/datetime'; import { DOWN_ARROW } from '@ptsecurity/cdk/keycodes'; import { ThemePalette } from '@ptsecurity/mosaic/core'; diff --git a/packages/mosaic/datepicker/datepicker-module.ts b/packages/mosaic/datepicker/datepicker-module.ts index a9b3c8d34..e6bfc0e41 100644 --- a/packages/mosaic/datepicker/datepicker-module.ts +++ b/packages/mosaic/datepicker/datepicker-module.ts @@ -1,8 +1,8 @@ +import { OverlayModule } from '@angular/cdk/overlay'; +import { PortalModule } from '@angular/cdk/portal'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; -import { PortalModule } from '@ptsecurity/cdk/portal'; import { McButtonModule } from '@ptsecurity/mosaic/button'; import { McIconModule } from '@ptsecurity/mosaic/icon'; diff --git a/packages/mosaic/datepicker/datepicker-toggle.ts b/packages/mosaic/datepicker/datepicker-toggle.ts index 58be8f921..847659a43 100644 --- a/packages/mosaic/datepicker/datepicker-toggle.ts +++ b/packages/mosaic/datepicker/datepicker-toggle.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { AfterContentInit, Attribute, @@ -13,7 +14,6 @@ import { ViewEncapsulation, ViewChild } from '@angular/core'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { McButton } from '@ptsecurity/mosaic/button'; import { merge, of as observableOf, Subscription } from 'rxjs'; diff --git a/packages/mosaic/datepicker/datepicker.spec.ts b/packages/mosaic/datepicker/datepicker.spec.ts index d87f116ea..db5c4a059 100644 --- a/packages/mosaic/datepicker/datepicker.spec.ts +++ b/packages/mosaic/datepicker/datepicker.spec.ts @@ -2,17 +2,17 @@ // tslint:disable:no-unbound-method // tslint:disable:no-typeof-undefined // tslint:disable:no-empty +import { Directionality } from '@angular/cdk/bidi'; +import { Overlay, OverlayContainer } from '@angular/cdk/overlay'; +import { ScrollDispatcher } from '@angular/cdk/scrolling'; import { Component, FactoryProvider, Type, ValueProvider, ViewChild } from '@angular/core'; import { ComponentFixture, fakeAsync, flush, inject, TestBed } from '@angular/core/testing'; import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { MC_DATE_LOCALE } from '@ptsecurity/cdk/datetime'; import { DOWN_ARROW, ENTER, ESCAPE, UP_ARROW } from '@ptsecurity/cdk/keycodes'; -import { Overlay, OverlayContainer } from '@ptsecurity/cdk/overlay'; -import { ScrollDispatcher } from '@ptsecurity/cdk/scrolling'; import { createKeyboardEvent, dispatchEvent, diff --git a/packages/mosaic/datepicker/datepicker.ts b/packages/mosaic/datepicker/datepicker.ts index 468e71968..1a915df29 100644 --- a/packages/mosaic/datepicker/datepicker.ts +++ b/packages/mosaic/datepicker/datepicker.ts @@ -1,5 +1,15 @@ // tslint:disable:no-unbound-method // tslint:disable:no-magic-numbers +import { Directionality } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { + Overlay, + OverlayConfig, + OverlayRef, + PositionStrategy, + ScrollStrategy +} from '@angular/cdk/overlay'; +import { ComponentPortal, ComponentType } from '@angular/cdk/portal'; import { DOCUMENT } from '@angular/common'; import { AfterViewInit, @@ -19,18 +29,8 @@ import { ViewContainerRef, ViewEncapsulation } from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { DateAdapter } from '@ptsecurity/cdk/datetime'; import { ESCAPE, UP_ARROW } from '@ptsecurity/cdk/keycodes'; -import { - Overlay, - OverlayConfig, - OverlayRef, - IPositionStrategy, - IScrollStrategy -} from '@ptsecurity/cdk/overlay'; -import { ComponentPortal, IComponentType } from '@ptsecurity/cdk/portal'; import { CanColor, CanColorCtor, @@ -53,11 +53,11 @@ let datepickerUid = 0; /** Injection token that determines the scroll handling while the calendar is open. */ export const MC_DATEPICKER_SCROLL_STRATEGY = - new InjectionToken<() => IScrollStrategy>('mc-datepicker-scroll-strategy'); + new InjectionToken<() => ScrollStrategy>('mc-datepicker-scroll-strategy'); /** @docs-private */ // tslint:disable-next-line:naming-convention -export function MC_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay: Overlay): () => IScrollStrategy { +export function MC_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay: Overlay): () => ScrollStrategy { return () => overlay.scrollStrategies.reposition(); } @@ -223,7 +223,7 @@ export class McDatepicker implements OnDestroy, CanColor { } /** An input indicating the type of the custom header component for the calendar, if set. */ - @Input() calendarHeaderComponent: IComponentType; + @Input() calendarHeaderComponent: ComponentType; /** The view that the calendar should start in. */ @Input() startView: 'month' | 'year' | 'multi-year' = 'month'; @@ -268,7 +268,7 @@ export class McDatepicker implements OnDestroy, CanColor { /** Emits new selected date when selected date changes. */ readonly selectedChanged = new Subject(); - private scrollStrategy: () => IScrollStrategy; + private scrollStrategy: () => ScrollStrategy; private _startAt: D | null; private _disabled: boolean; private _opened = false; @@ -451,7 +451,7 @@ export class McDatepicker implements OnDestroy, CanColor { } /** Create the popup PositionStrategy. */ - private createPopupPositionStrategy(): IPositionStrategy { + private createPopupPositionStrategy(): PositionStrategy { return this.overlay.position() .flexibleConnectedTo(this.datepickerInput.elementRef) .withTransformOriginOn('.mc-datepicker__content') diff --git a/packages/mosaic/datepicker/month-view.spec.ts b/packages/mosaic/datepicker/month-view.spec.ts index 27a0c169b..2372dae6e 100644 --- a/packages/mosaic/datepicker/month-view.spec.ts +++ b/packages/mosaic/datepicker/month-view.spec.ts @@ -1,8 +1,8 @@ // tslint:disable:no-magic-numbers +import { Direction, Directionality } from '@angular/cdk/bidi'; import { Component } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; import { DOWN_ARROW, END, diff --git a/packages/mosaic/datepicker/month-view.ts b/packages/mosaic/datepicker/month-view.ts index 438d6572c..8343890a5 100644 --- a/packages/mosaic/datepicker/month-view.ts +++ b/packages/mosaic/datepicker/month-view.ts @@ -1,4 +1,5 @@ // tslint:disable:no-magic-numbers +import { Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, ChangeDetectionStrategy, @@ -12,7 +13,6 @@ import { ViewEncapsulation, ViewChild } from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { DateAdapter, MC_DATE_FORMATS, McDateFormats } from '@ptsecurity/cdk/datetime'; import { DOWN_ARROW, diff --git a/packages/mosaic/datepicker/multi-year-view.spec.ts b/packages/mosaic/datepicker/multi-year-view.spec.ts index f8262695b..9547f9057 100644 --- a/packages/mosaic/datepicker/multi-year-view.spec.ts +++ b/packages/mosaic/datepicker/multi-year-view.spec.ts @@ -1,8 +1,8 @@ // tslint:disable:no-magic-numbers +import { Direction, Directionality } from '@angular/cdk/bidi'; import { Component, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; import { DOWN_ARROW, END, diff --git a/packages/mosaic/datepicker/multi-year-view.ts b/packages/mosaic/datepicker/multi-year-view.ts index 3e66e1042..35cac370c 100644 --- a/packages/mosaic/datepicker/multi-year-view.ts +++ b/packages/mosaic/datepicker/multi-year-view.ts @@ -1,4 +1,5 @@ // tslint:disable:no-magic-numbers +import { Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, ChangeDetectionStrategy, @@ -11,7 +12,6 @@ import { ViewChild, ViewEncapsulation } from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { DateAdapter } from '@ptsecurity/cdk/datetime'; import { DOWN_ARROW, diff --git a/packages/mosaic/datepicker/year-view.spec.ts b/packages/mosaic/datepicker/year-view.spec.ts index c0453fb92..f010e9efc 100644 --- a/packages/mosaic/datepicker/year-view.spec.ts +++ b/packages/mosaic/datepicker/year-view.spec.ts @@ -1,8 +1,8 @@ // tslint:disable:no-magic-numbers +import { Direction, Directionality } from '@angular/cdk/bidi'; import { Component, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; import { DOWN_ARROW, END, diff --git a/packages/mosaic/datepicker/year-view.ts b/packages/mosaic/datepicker/year-view.ts index b02dfee17..5ce8c6d59 100644 --- a/packages/mosaic/datepicker/year-view.ts +++ b/packages/mosaic/datepicker/year-view.ts @@ -1,3 +1,4 @@ +import { Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, ChangeDetectionStrategy, @@ -11,7 +12,6 @@ import { ViewChild, ViewEncapsulation } from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { DateAdapter, MC_DATE_FORMATS, McDateFormats } from '@ptsecurity/cdk/datetime'; import { DOWN_ARROW, diff --git a/packages/mosaic/divider/divider.component.ts b/packages/mosaic/divider/divider.component.ts index 2450229a2..f271ca7b2 100644 --- a/packages/mosaic/divider/divider.component.ts +++ b/packages/mosaic/divider/divider.component.ts @@ -1,5 +1,5 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; @Component({ diff --git a/packages/mosaic/dropdown/dropdown-content.ts b/packages/mosaic/dropdown/dropdown-content.ts index ff77e8f9d..d246eadac 100644 --- a/packages/mosaic/dropdown/dropdown-content.ts +++ b/packages/mosaic/dropdown/dropdown-content.ts @@ -1,3 +1,4 @@ +import { TemplatePortal, DomPortalOutlet } from '@angular/cdk/portal'; import { DOCUMENT } from '@angular/common'; import { Directive, @@ -9,7 +10,6 @@ import { Inject, OnDestroy } from '@angular/core'; -import { TemplatePortal, DomPortalOutlet } from '@ptsecurity/cdk/portal'; import { Subject } from 'rxjs'; diff --git a/packages/mosaic/dropdown/dropdown-panel.ts b/packages/mosaic/dropdown/dropdown-panel.ts index df367dca2..a6690b913 100644 --- a/packages/mosaic/dropdown/dropdown-panel.ts +++ b/packages/mosaic/dropdown/dropdown-panel.ts @@ -1,6 +1,6 @@ +import { Direction } from '@angular/cdk/bidi'; import { EventEmitter, TemplateRef, InjectionToken } from '@angular/core'; import { FocusOrigin } from '@ptsecurity/cdk/a11y'; -import { Direction } from '@ptsecurity/cdk/bidi'; import { McDropdownContent } from './dropdown-content'; import { DropdownPositionX, DropdownPositionY } from './dropdown-positions'; diff --git a/packages/mosaic/dropdown/dropdown-trigger.ts b/packages/mosaic/dropdown/dropdown-trigger.ts index 70f67bdec..baebc0f74 100644 --- a/packages/mosaic/dropdown/dropdown-trigger.ts +++ b/packages/mosaic/dropdown/dropdown-trigger.ts @@ -1,3 +1,15 @@ +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { + FlexibleConnectedPositionStrategy, + HorizontalConnectionPos, + Overlay, + OverlayConfig, + OverlayRef, + VerticalConnectionPos, + ScrollStrategy +} from '@angular/cdk/overlay'; +import { normalizePassiveListenerOptions } from '@angular/cdk/platform'; +import { TemplatePortal } from '@angular/cdk/portal'; import { AfterContentInit, Directive, @@ -13,19 +25,7 @@ import { ViewContainerRef } from '@angular/core'; import { FocusMonitor, FocusOrigin } from '@ptsecurity/cdk/a11y'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; import { LEFT_ARROW, RIGHT_ARROW, SPACE, ENTER } from '@ptsecurity/cdk/keycodes'; -import { - FlexibleConnectedPositionStrategy, - HorizontalConnectionPos, - Overlay, - OverlayConfig, - OverlayRef, - VerticalConnectionPos, - IScrollStrategy -} from '@ptsecurity/cdk/overlay'; -import { normalizePassiveListenerOptions } from '@ptsecurity/cdk/platform'; -import { TemplatePortal } from '@ptsecurity/cdk/portal'; import { asapScheduler, merge, of as observableOf, Subscription } from 'rxjs'; import { delay, filter, take, takeUntil } from 'rxjs/operators'; @@ -38,10 +38,10 @@ import { McDropdown } from './dropdown.component'; /** Injection token that determines the scroll handling while the dropdown is open. */ export const MC_DROPDOWN_SCROLL_STRATEGY = - new InjectionToken<() => IScrollStrategy>('mc-dropdown-scroll-strategy'); + new InjectionToken<() => ScrollStrategy>('mc-dropdown-scroll-strategy'); /** @docs-private */ -export function MC_DROPDOWN_SCROLL_STRATEGY_FACTORY(overlay: Overlay): () => IScrollStrategy { +export function MC_DROPDOWN_SCROLL_STRATEGY_FACTORY(overlay: Overlay): () => ScrollStrategy { return () => overlay.scrollStrategies.reposition(); } diff --git a/packages/mosaic/dropdown/dropdown.component.ts b/packages/mosaic/dropdown/dropdown.component.ts index 3effdbc0c..5792821a1 100644 --- a/packages/mosaic/dropdown/dropdown.component.ts +++ b/packages/mosaic/dropdown/dropdown.component.ts @@ -1,4 +1,6 @@ import { AnimationEvent } from '@angular/animations'; +import { Direction } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { AfterContentInit, ChangeDetectionStrategy, @@ -20,8 +22,6 @@ import { OnInit } from '@angular/core'; import { FocusKeyManager, FocusOrigin } from '@ptsecurity/cdk/a11y'; -import { Direction } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { ESCAPE, LEFT_ARROW, RIGHT_ARROW, DOWN_ARROW, UP_ARROW } from '@ptsecurity/cdk/keycodes'; import { merge, Observable, Subject, Subscription } from 'rxjs'; import { startWith, switchMap, take } from 'rxjs/operators'; @@ -175,6 +175,11 @@ export class McDropdown implements AfterContentInit, McDropdownPanel = new EventEmitter(); + private _previousPanelClass: string; + private _keyManager: FocusKeyManager; - private _xPosition: DropdownPositionX = this._defaultOptions.xPosition; - private _yPosition: DropdownPositionY = this._defaultOptions.yPosition; /** Dropdown items inside the current dropdown. */ private _items: McDropdownItem[] = []; @@ -229,9 +232,6 @@ export class McDropdown implements AfterContentInit, McDropdownPanel, @@ -256,7 +256,7 @@ export class McDropdown implements AfterContentInit, McDropdownPanel { return this._itemChanges.pipe( startWith(this._items), - switchMap(items => merge(...items.map(item => item._hovered))) + switchMap((items) => merge(...items.map((item) => item._hovered))) ); } diff --git a/packages/mosaic/dropdown/dropdown.module.ts b/packages/mosaic/dropdown/dropdown.module.ts index 9a1de1856..3962302da 100644 --- a/packages/mosaic/dropdown/dropdown.module.ts +++ b/packages/mosaic/dropdown/dropdown.module.ts @@ -1,6 +1,6 @@ +import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; import { McIconModule } from '@ptsecurity/mosaic/icon'; import { McDropdownContent } from './dropdown-content'; diff --git a/packages/mosaic/dropdown/dropdown.spec.ts b/packages/mosaic/dropdown/dropdown.spec.ts index eb82a2c3f..27a1f0a8e 100644 --- a/packages/mosaic/dropdown/dropdown.spec.ts +++ b/packages/mosaic/dropdown/dropdown.spec.ts @@ -2,6 +2,9 @@ // tslint:disable:prefer-array-literal // tslint:disable:no-empty +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { Overlay, OverlayContainer } from '@angular/cdk/overlay'; +import { ScrollDispatcher } from '@angular/cdk/scrolling'; import { Component, ElementRef, @@ -20,10 +23,7 @@ import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick } from '@angu import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { FocusMonitor } from '@ptsecurity/cdk/a11y'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; import { DOWN_ARROW, ESCAPE, LEFT_ARROW, RIGHT_ARROW, TAB } from '@ptsecurity/cdk/keycodes'; -import { Overlay, OverlayContainer } from '@ptsecurity/cdk/overlay'; -import { ScrollDispatcher } from '@ptsecurity/cdk/scrolling'; import { createKeyboardEvent, createMouseEvent, @@ -915,7 +915,7 @@ describe('McDropdown', () => { readonly fixture: ComponentFixture; readonly trigger: HTMLElement; - constructor(ctor: { new(): T; }, inputs: { [key: string]: any } = {}) { + constructor(ctor: new() => T, inputs: { [key: string]: any } = {}) { this.fixture = createComponent(ctor); Object.keys(inputs) .forEach((key) => (this.fixture.componentInstance as any)[key] = inputs[key]); diff --git a/packages/mosaic/icon/icon.module.ts b/packages/mosaic/icon/icon.module.ts index 7eeca7921..e80119bdf 100644 --- a/packages/mosaic/icon/icon.module.ts +++ b/packages/mosaic/icon/icon.module.ts @@ -1,8 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; - import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { McIcon, diff --git a/packages/mosaic/input/input.ts b/packages/mosaic/input/input.ts index 0d02c3ea4..40239d5ce 100644 --- a/packages/mosaic/input/input.ts +++ b/packages/mosaic/input/input.ts @@ -1,3 +1,5 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { getSupportedInputTypes, Platform } from '@angular/cdk/platform'; import { Attribute, Directive, DoCheck, ElementRef, Inject, Input, OnChanges, @@ -8,13 +10,11 @@ import { NgControl, NgForm, NgModel } from '@angular/forms'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { END, C, V, X, A, DELETE, BACKSPACE, TAB, ENTER, ESCAPE, ZERO, NINE, NUMPAD_ZERO, NUMPAD_NINE, NUMPAD_MINUS, DASH, FF_MINUS, LEFT_ARROW, RIGHT_ARROW, HOME, UP_ARROW, DOWN_ARROW, F1, F12 } from '@ptsecurity/cdk/keycodes'; -import { getSupportedInputTypes, Platform } from '@ptsecurity/cdk/platform'; import { CanUpdateErrorState, CanUpdateErrorStateCtor, diff --git a/packages/mosaic/list/list-selection.component.ts b/packages/mosaic/list/list-selection.component.ts index b70903a11..03609be8d 100644 --- a/packages/mosaic/list/list-selection.component.ts +++ b/packages/mosaic/list/list-selection.component.ts @@ -1,3 +1,4 @@ +import { SelectionModel } from '@angular/cdk/collections'; import { AfterContentInit, Attribute, @@ -19,7 +20,6 @@ import { } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FocusKeyManager, IFocusableOption } from '@ptsecurity/cdk/a11y'; -import { SelectionModel } from '@ptsecurity/cdk/collections'; import { DOWN_ARROW, END, diff --git a/packages/mosaic/modal/modal.component.html b/packages/mosaic/modal/modal.component.html index e87d64e12..0d3c92abc 100644 --- a/packages/mosaic/modal/modal.component.html +++ b/packages/mosaic/modal/modal.component.html @@ -27,7 +27,7 @@ [style.transform-origin]="transformOrigin" role="document" > -
      +
      - @@ -119,7 +119,7 @@ -
      diff --git a/packages/mosaic/modal/modal.component.ts b/packages/mosaic/modal/modal.component.ts index e5d4b185c..fe4bfd4a4 100644 --- a/packages/mosaic/modal/modal.component.ts +++ b/packages/mosaic/modal/modal.component.ts @@ -1,3 +1,4 @@ +import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { DOCUMENT } from '@angular/common'; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, @@ -21,7 +22,6 @@ import { ViewContainerRef, ViewEncapsulation } from '@angular/core'; import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { Overlay, OverlayRef } from '@ptsecurity/cdk/overlay'; import { McMeasureScrollbarService } from '@ptsecurity/mosaic/core'; import { Observable } from 'rxjs'; diff --git a/packages/mosaic/modal/modal.module.ts b/packages/mosaic/modal/modal.module.ts index 12a8b1156..a73aa4d85 100644 --- a/packages/mosaic/modal/modal.module.ts +++ b/packages/mosaic/modal/modal.module.ts @@ -1,7 +1,7 @@ +import { A11yModule } from '@angular/cdk/a11y'; +import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; - -import { OverlayModule } from '@ptsecurity/cdk/overlay'; import { McButtonModule } from '@ptsecurity/mosaic/button'; import { McIconModule } from '@ptsecurity/mosaic/icon'; @@ -12,10 +12,10 @@ import { McModalService } from './modal.service'; @NgModule({ - imports: [ CommonModule, OverlayModule, McButtonModule, McIconModule ], - exports: [ McModalComponent ], - declarations: [ McModalComponent, CssUnitPipe ], - entryComponents: [ McModalComponent ], - providers: [ McModalControlService, McModalService ] + imports: [CommonModule, OverlayModule, A11yModule, McButtonModule, McIconModule], + exports: [McModalComponent], + declarations: [McModalComponent, CssUnitPipe], + entryComponents: [McModalComponent], + providers: [McModalControlService, McModalService] }) -export class McModalModule { } +export class McModalModule {} diff --git a/packages/mosaic/modal/modal.service.ts b/packages/mosaic/modal/modal.service.ts index bde88bd37..7c520c2a2 100644 --- a/packages/mosaic/modal/modal.service.ts +++ b/packages/mosaic/modal/modal.service.ts @@ -1,12 +1,10 @@ +import { Overlay, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; import { ComponentRef, Injectable } from '@angular/core'; - +import { ESCAPE } from '@ptsecurity/cdk/keycodes'; import { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; -import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { Overlay, OverlayRef } from '@ptsecurity/cdk/overlay'; -import { ComponentPortal } from '@ptsecurity/cdk/portal'; - import { McModalControlService } from './modal-control.service'; import { McModalRef } from './modal-ref.class'; import { McModalComponent } from './modal.component'; diff --git a/packages/mosaic/modal/modal.spec.ts b/packages/mosaic/modal/modal.spec.ts index 8fedaa151..cafc8e4e6 100644 --- a/packages/mosaic/modal/modal.spec.ts +++ b/packages/mosaic/modal/modal.spec.ts @@ -1,6 +1,6 @@ +import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, EventEmitter, NgModule } from '@angular/core'; import { async, ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing'; -import { OverlayContainer } from '@ptsecurity/cdk/overlay'; import { McMeasureScrollbarService } from '@ptsecurity/mosaic/core'; import { McModalControlService } from './modal-control.service'; diff --git a/packages/mosaic/modal/modal.type.ts b/packages/mosaic/modal/modal.type.ts index a193628cf..d0c2e10a6 100644 --- a/packages/mosaic/modal/modal.type.ts +++ b/packages/mosaic/modal/modal.type.ts @@ -1,7 +1,6 @@ +import { OverlayRef } from '@angular/cdk/overlay'; import { EventEmitter, TemplateRef, Type } from '@angular/core'; -import { OverlayRef } from '@ptsecurity/cdk/overlay'; - export type OnClickCallback = ((instance: T) => (false | void | {}) | Promise); diff --git a/packages/mosaic/navbar/navbar.module.ts b/packages/mosaic/navbar/navbar.module.ts index 092d59087..d85f40f2e 100644 --- a/packages/mosaic/navbar/navbar.module.ts +++ b/packages/mosaic/navbar/navbar.module.ts @@ -1,7 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { McNavbar, diff --git a/packages/mosaic/package.json b/packages/mosaic/package.json index 93d3b0bed..d95631d30 100644 --- a/packages/mosaic/package.json +++ b/packages/mosaic/package.json @@ -25,6 +25,7 @@ "@angular/core": "0.0.0-NG", "@angular/common": "0.0.0-NG", "@angular/forms": "0.0.0-NG", + "@angular/cdk": "0.0.0-NG", "@ptsecurity/cdk": "0.0.0-PLACEHOLDER", "@ptsecurity/mosaic-moment-adapter": "0.0.0-PLACEHOLDER" }, diff --git a/packages/mosaic/popover/popover.component.ts b/packages/mosaic/popover/popover.component.ts index 92474bd3e..dc2bc5079 100644 --- a/packages/mosaic/popover/popover.component.ts +++ b/packages/mosaic/popover/popover.component.ts @@ -1,4 +1,20 @@ import { AnimationEvent } from '@angular/animations'; +import { Directionality } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { + ConnectedOverlayPositionChange, + ConnectionPositionPair, + Overlay, + OverlayRef, + ScrollDispatcher, + ScrollStrategy, + FlexibleConnectedPositionStrategy, + OverlayConnectionPosition, + OriginConnectionPosition, + HorizontalConnectionPos, + VerticalConnectionPos +} from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -18,23 +34,7 @@ import { ViewContainerRef, ViewEncapsulation } from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { - ConnectedOverlayPositionChange, - ConnectionPositionPair, - Overlay, - OverlayRef, - ScrollDispatcher, - IScrollStrategy, - FlexibleConnectedPositionStrategy, - IOverlayConnectionPosition, - IOriginConnectionPosition, - HorizontalConnectionPos, - VerticalConnectionPos -} from '@ptsecurity/cdk/overlay'; -import { ComponentPortal } from '@ptsecurity/cdk/portal'; import { EXTENDED_OVERLAY_POSITIONS, POSITION_MAP, @@ -221,10 +221,10 @@ export class McPopoverComponent { } export const MC_POPOVER_SCROLL_STRATEGY = - new InjectionToken<() => IScrollStrategy>('mc-popover-scroll-strategy'); + new InjectionToken<() => ScrollStrategy>('mc-popover-scroll-strategy'); /** @docs-private */ -export function mcPopoverScrollStrategyFactory(overlay: Overlay): () => IScrollStrategy { +export function mcPopoverScrollStrategyFactory(overlay: Overlay): () => ScrollStrategy { return () => overlay.scrollStrategies.reposition({scrollThrottle: 20}); } @@ -665,8 +665,8 @@ export class McPopover implements OnInit, OnDestroy { * Returns the origin position and a fallback position based on the user's position preference. * The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`). */ - getOrigin(): {main: IOriginConnectionPosition; fallback: IOriginConnectionPosition} { - let originPosition: IOriginConnectionPosition; + getOrigin(): {main: OriginConnectionPosition; fallback: OriginConnectionPosition} { + let originPosition: OriginConnectionPosition; const originXPosition = this.getOriginXaxis(); const originYPosition = this.getOriginYaxis(); originPosition = {originX: originXPosition, originY: originYPosition}; @@ -715,9 +715,9 @@ export class McPopover implements OnInit, OnDestroy { } /** Returns the overlay position and a fallback position based on the user's preference */ - getOverlayPosition(): {main: IOverlayConnectionPosition; fallback: IOverlayConnectionPosition} { + getOverlayPosition(): {main: OverlayConnectionPosition; fallback: OverlayConnectionPosition} { const position = this.mcPlacement; - let overlayPosition: IOverlayConnectionPosition; + let overlayPosition: OverlayConnectionPosition; if (this.availablePositions[position]) { overlayPosition = { overlayX : this.availablePositions[position].overlayX, diff --git a/packages/mosaic/popover/popover.module.ts b/packages/mosaic/popover/popover.module.ts index 2dd3c99d3..1049ca3e6 100644 --- a/packages/mosaic/popover/popover.module.ts +++ b/packages/mosaic/popover/popover.module.ts @@ -1,6 +1,6 @@ +import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; import { McPopoverComponent, diff --git a/packages/mosaic/popover/popover.spec.ts b/packages/mosaic/popover/popover.spec.ts index c3efc471a..91d3cf504 100644 --- a/packages/mosaic/popover/popover.spec.ts +++ b/packages/mosaic/popover/popover.spec.ts @@ -1,8 +1,8 @@ +import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, ElementRef, ViewChild } from '@angular/core'; import { fakeAsync, inject, tick, TestBed, flush } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { OverlayContainer } from '@ptsecurity/cdk/overlay'; import { dispatchMouseEvent, dispatchFakeEvent } from '@ptsecurity/cdk/testing'; import { McPopoverModule } from './popover.module'; diff --git a/packages/mosaic/progress-bar/progress-bar.module.ts b/packages/mosaic/progress-bar/progress-bar.module.ts index aabb14a73..588963202 100644 --- a/packages/mosaic/progress-bar/progress-bar.module.ts +++ b/packages/mosaic/progress-bar/progress-bar.module.ts @@ -1,8 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; - import { McProgressBar } from './progress-bar.component'; diff --git a/packages/mosaic/progress-spinner/progress-spinner.module.ts b/packages/mosaic/progress-spinner/progress-spinner.module.ts index b4b2b82d6..25945e626 100644 --- a/packages/mosaic/progress-spinner/progress-spinner.module.ts +++ b/packages/mosaic/progress-spinner/progress-spinner.module.ts @@ -1,8 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; - import { McProgressSpinner } from './progress-spinner.component'; diff --git a/packages/mosaic/radio/radio.component.ts b/packages/mosaic/radio/radio.component.ts index 90247ebd3..c3fb5e64b 100644 --- a/packages/mosaic/radio/radio.component.ts +++ b/packages/mosaic/radio/radio.component.ts @@ -1,3 +1,4 @@ +import { UniqueSelectionDispatcher } from '@angular/cdk/collections'; import { AfterContentInit, ChangeDetectionStrategy, @@ -8,9 +9,6 @@ import { ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; - -import { UniqueSelectionDispatcher } from '@ptsecurity/cdk/collections'; - import { CanColor, CanColorCtor, CanDisable, CanDisableCtor, @@ -311,25 +309,6 @@ export const _McRadioButtonMixinBase: export class McRadioButton extends _McRadioButtonMixinBase implements OnInit, OnDestroy, CanColor, HasTabIndex { - private readonly _uniqueId: string = `mc-radio-${++nextUniqueId}`; - - /* tslint:disable:member-ordering */ - - /** The unique ID for the radio button. */ - @Input() id: string = this._uniqueId; - - /** Analog to HTML 'name' attribute used to group radios for unique selection. */ - @Input() name: string; - - /** Used to set the 'aria-label' attribute on the underlying input element. */ - @Input('aria-label') ariaLabel: string; - - /** The 'aria-labelledby' attribute takes precedence as the element's text alternative. */ - @Input('aria-labelledby') ariaLabelledby: string; - - /** The 'aria-describedby' attribute is read after the element's label and field type. */ - @Input('aria-describedby') ariaDescribedby: string; - /** Whether this radio button is checked. */ @Input() get checked(): boolean { return this._checked; } @@ -407,21 +386,11 @@ export class McRadioButton extends _McRadioButtonMixinBase } private _labelPosition: 'before' | 'after'; - /** The native `` element */ - @ViewChild('input', {static: false}) _inputElement: ElementRef; - - /** - * Event emitted when the checked state of this radio button changes. - * Change events are only emitted when the value changes due to user interaction with - * the radio button (the same behavior as ``). - */ - @Output() readonly change: EventEmitter = new EventEmitter(); - - /** The parent radio group. May or may not be present. */ - radioGroup: McRadioGroup; + /* tslint:disable:member-ordering */ + private readonly _uniqueId: string = `mc-radio-${++nextUniqueId}`; - @Input() - isFocused: boolean = false; + /** The unique ID for the radio button. */ + @Input() id: string = this._uniqueId; /** ID of the native input element inside `` */ get inputId(): string { return `${this.id || this._uniqueId}-input`; } @@ -438,9 +407,33 @@ export class McRadioButton extends _McRadioButtonMixinBase /** Value assigned to this radio. */ private _value: any = null; - /** Unregister function for _radioDispatcher */ - // tslint:disable-next-line - private readonly removeUniqueSelectionListener: () => void = () => {}; + /** Analog to HTML 'name' attribute used to group radios for unique selection. */ + @Input() name: string; + + /** Used to set the 'aria-label' attribute on the underlying input element. */ + @Input('aria-label') ariaLabel: string; + + /** The 'aria-labelledby' attribute takes precedence as the element's text alternative. */ + @Input('aria-labelledby') ariaLabelledby: string; + + /** The 'aria-describedby' attribute is read after the element's label and field type. */ + @Input('aria-describedby') ariaDescribedby: string; + + /** The native `` element */ + @ViewChild('input', {static: false}) _inputElement: ElementRef; + + /** + * Event emitted when the checked state of this radio button changes. + * Change events are only emitted when the value changes due to user interaction with + * the radio button (the same behavior as ``). + */ + @Output() readonly change: EventEmitter = new EventEmitter(); + + /** The parent radio group. May or may not be present. */ + radioGroup: McRadioGroup; + + @Input() + isFocused: boolean = false; constructor( @Optional() radioGroup: McRadioGroup, @@ -519,6 +512,10 @@ export class McRadioButton extends _McRadioButtonMixinBase } } + /** Unregister function for _radioDispatcher */ + // tslint:disable-next-line + private readonly removeUniqueSelectionListener: () => void = () => {}; + /** Dispatch change event with current value. */ private emitChangeEvent(): void { this.change.emit(new McRadioChange(this, this._value)); diff --git a/packages/mosaic/select/select.component.spec.ts b/packages/mosaic/select/select.component.spec.ts index 576021de1..415c1d12c 100644 --- a/packages/mosaic/select/select.component.spec.ts +++ b/packages/mosaic/select/select.component.spec.ts @@ -5,6 +5,10 @@ /* tslint:disable:no-unbound-method */ /* tslint:disable:prefer-for-of */ +import { Directionality } from '@angular/cdk/bidi'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { Platform } from '@angular/cdk/platform'; +import { ScrollDispatcher, ViewportRuler } from '@angular/cdk/scrolling'; import { ChangeDetectionStrategy, Component, @@ -35,7 +39,6 @@ import { } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { DOWN_ARROW, END, @@ -48,9 +51,6 @@ import { UP_ARROW, A, ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { OverlayContainer } from '@ptsecurity/cdk/overlay'; -import { Platform } from '@ptsecurity/cdk/platform'; -import { ScrollDispatcher, ViewportRuler } from '@ptsecurity/cdk/scrolling'; import { createKeyboardEvent, dispatchEvent, diff --git a/packages/mosaic/select/select.component.ts b/packages/mosaic/select/select.component.ts index e206aace7..ef3ea1cc7 100644 --- a/packages/mosaic/select/select.component.ts +++ b/packages/mosaic/select/select.component.ts @@ -1,5 +1,12 @@ /* tslint:disable:no-empty */ +import { Directionality } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { SelectionModel } from '@angular/cdk/collections'; +import { + CdkConnectedOverlay, + ViewportRuler +} from '@angular/cdk/overlay'; import { AfterContentInit, AfterViewInit, @@ -32,9 +39,6 @@ import { } from '@angular/core'; import { ControlValueAccessor, FormGroupDirective, NgControl, NgForm } from '@angular/forms'; import { ActiveDescendantKeyManager } from '@ptsecurity/cdk/a11y'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; -import { SelectionModel } from '@ptsecurity/cdk/collections'; import { DOWN_ARROW, END, @@ -49,10 +53,6 @@ import { PAGE_UP, PAGE_DOWN } from '@ptsecurity/cdk/keycodes'; -import { - CdkConnectedOverlay, - ViewportRuler -} from '@ptsecurity/cdk/overlay'; import { countGroupLabelsBeforeOption, getOptionScrollPosition, diff --git a/packages/mosaic/select/select.module.ts b/packages/mosaic/select/select.module.ts index b1353a87b..b7894f0a7 100644 --- a/packages/mosaic/select/select.module.ts +++ b/packages/mosaic/select/select.module.ts @@ -1,6 +1,6 @@ +import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; import { MC_SELECT_SCROLL_STRATEGY_PROVIDER, McOptionModule } from '@ptsecurity/mosaic/core'; import { McFormFieldModule } from '@ptsecurity/mosaic/form-field'; import { McIconModule } from '@ptsecurity/mosaic/icon'; diff --git a/packages/mosaic/sidepanel/sidepanel-container.component.ts b/packages/mosaic/sidepanel/sidepanel-container.component.ts index 705e9d5e8..6c702c9cf 100644 --- a/packages/mosaic/sidepanel/sidepanel-container.component.ts +++ b/packages/mosaic/sidepanel/sidepanel-container.component.ts @@ -1,4 +1,5 @@ import { AnimationEvent } from '@angular/animations'; +import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal } from '@angular/cdk/portal'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -12,14 +13,11 @@ import { ViewEncapsulation } from '@angular/core'; -import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal } from '@ptsecurity/cdk/portal'; - import { mcSidepanelAnimations, McSidepanelAnimationState, mcSidepanelTransformAnimation } from './sidepanel-animations'; - import { McSidepanelConfig, McSidepanelPosition } from './sidepanel-config'; diff --git a/packages/mosaic/sidepanel/sidepanel-ref.ts b/packages/mosaic/sidepanel/sidepanel-ref.ts index 1669303a4..e9e2ce846 100644 --- a/packages/mosaic/sidepanel/sidepanel-ref.ts +++ b/packages/mosaic/sidepanel/sidepanel-ref.ts @@ -1,5 +1,5 @@ +import { OverlayRef } from '@angular/cdk/overlay'; import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { OverlayRef } from '@ptsecurity/cdk/overlay'; import { merge, Observable, Subject } from 'rxjs'; import { filter, take } from 'rxjs/operators'; diff --git a/packages/mosaic/sidepanel/sidepanel.module.ts b/packages/mosaic/sidepanel/sidepanel.module.ts index 3db0ad85e..67002161c 100644 --- a/packages/mosaic/sidepanel/sidepanel.module.ts +++ b/packages/mosaic/sidepanel/sidepanel.module.ts @@ -1,7 +1,7 @@ +import { OverlayModule } from '@angular/cdk/overlay'; +import { PortalModule } from '@angular/cdk/portal'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; -import { PortalModule } from '@ptsecurity/cdk/portal'; import { McCommonModule } from '@ptsecurity/mosaic/core'; import { McIconModule } from '@ptsecurity/mosaic/icon'; diff --git a/packages/mosaic/sidepanel/sidepanel.service.ts b/packages/mosaic/sidepanel/sidepanel.service.ts index c43a917ae..9727e614d 100644 --- a/packages/mosaic/sidepanel/sidepanel.service.ts +++ b/packages/mosaic/sidepanel/sidepanel.service.ts @@ -1,3 +1,5 @@ +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal, ComponentType, PortalInjector, TemplatePortal } from '@angular/cdk/portal'; import { ComponentRef, Inject, @@ -8,8 +10,6 @@ import { SkipSelf, TemplateRef } from '@angular/core'; -import { Overlay, OverlayConfig, OverlayRef } from '@ptsecurity/cdk/overlay'; -import { ComponentPortal, IComponentType, PortalInjector, TemplatePortal } from '@ptsecurity/cdk/portal'; import { MC_SIDEPANEL_DATA, McSidepanelConfig } from './sidepanel-config'; import { @@ -47,7 +47,7 @@ export class McSidepanelService implements OnDestroy { this.closeSidepanels(this.openedSidepanelsAtThisLevel); } - open(componentOrTemplateRef: IComponentType | TemplateRef, + open(componentOrTemplateRef: ComponentType | TemplateRef, config?: McSidepanelConfig): McSidepanelRef { const fullConfig = { ...(this.defaultOptions || new McSidepanelConfig()), diff --git a/packages/mosaic/sidepanel/sidepanel.spec.ts b/packages/mosaic/sidepanel/sidepanel.spec.ts index 8fb04d8b7..b72b1c7e7 100644 --- a/packages/mosaic/sidepanel/sidepanel.spec.ts +++ b/packages/mosaic/sidepanel/sidepanel.spec.ts @@ -1,8 +1,8 @@ +import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, Inject, NgModule, TemplateRef, ViewChild } from '@angular/core'; import { ComponentFixture, fakeAsync, flush, inject, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { OverlayContainer } from '@ptsecurity/cdk/overlay'; import { dispatchKeyboardEvent } from '@ptsecurity/cdk/testing'; import { MC_SIDEPANEL_DATA, McSidepanelModule, McSidepanelPosition, McSidepanelRef, McSidepanelService } from './index'; diff --git a/packages/mosaic/splitter/splitter.component.ts b/packages/mosaic/splitter/splitter.component.ts index 757706b0b..fe21dabc6 100644 --- a/packages/mosaic/splitter/splitter.component.ts +++ b/packages/mosaic/splitter/splitter.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty, coerceCssPixelValue, coerceNumberProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, @@ -10,8 +11,6 @@ import { ViewEncapsulation } from '@angular/core'; -import { coerceBooleanProperty, coerceCssPixelValue, coerceNumberProperty } from '@ptsecurity/cdk/coercion'; - interface IArea { area: McSplitterAreaDirective; diff --git a/packages/mosaic/tabs/tab-body.spec.ts b/packages/mosaic/tabs/tab-body.spec.ts index 7a6a2bcf7..621e8f1ea 100644 --- a/packages/mosaic/tabs/tab-body.spec.ts +++ b/packages/mosaic/tabs/tab-body.spec.ts @@ -1,9 +1,9 @@ +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { PortalModule, TemplatePortal } from '@angular/cdk/portal'; import { CommonModule } from '@angular/common'; import { AfterContentInit, Component, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; -import { PortalModule, TemplatePortal } from '@ptsecurity/cdk/portal'; import { Subject } from 'rxjs'; import { McTabBody, McTabBodyPortal } from './tab-body'; diff --git a/packages/mosaic/tabs/tab-body.ts b/packages/mosaic/tabs/tab-body.ts index 99033a245..6bccef6d1 100644 --- a/packages/mosaic/tabs/tab-body.ts +++ b/packages/mosaic/tabs/tab-body.ts @@ -1,4 +1,6 @@ import { AnimationEvent } from '@angular/animations'; +import { Directionality, Direction } from '@angular/cdk/bidi'; +import { TemplatePortal, CdkPortalOutlet, PortalHostDirective } from '@angular/cdk/portal'; import { Component, ChangeDetectorRef, @@ -18,8 +20,6 @@ import { forwardRef, ViewChild } from '@angular/core'; -import { Directionality, Direction } from '@ptsecurity/cdk/bidi'; -import { TemplatePortal, CdkPortalOutlet, PortalHostDirective } from '@ptsecurity/cdk/portal'; import { Subscription } from 'rxjs'; import { startWith } from 'rxjs/operators'; diff --git a/packages/mosaic/tabs/tab-group.ts b/packages/mosaic/tabs/tab-group.ts index 96d1a53cc..96c1ccb20 100644 --- a/packages/mosaic/tabs/tab-group.ts +++ b/packages/mosaic/tabs/tab-group.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion'; import { AfterContentChecked, AfterContentInit, @@ -18,7 +19,6 @@ import { Optional, Directive, Attribute } from '@angular/core'; -import { coerceBooleanProperty, coerceNumberProperty } from '@ptsecurity/cdk/coercion'; import { CanColor, CanColorCtor, diff --git a/packages/mosaic/tabs/tab-header.spec.ts b/packages/mosaic/tabs/tab-header.spec.ts index 084064a9d..0ccd1fdaa 100644 --- a/packages/mosaic/tabs/tab-header.spec.ts +++ b/packages/mosaic/tabs/tab-header.spec.ts @@ -1,4 +1,7 @@ // tslint:disable:no-magic-numbers +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { PortalModule } from '@angular/cdk/portal'; +import { ScrollingModule, ViewportRuler } from '@angular/cdk/scrolling'; import { CommonModule } from '@angular/common'; import { Component, ViewChild } from '@angular/core'; import { @@ -9,7 +12,6 @@ import { TestBed, tick } from '@angular/core/testing'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; import { END, ENTER, @@ -18,8 +20,6 @@ import { RIGHT_ARROW, SPACE } from '@ptsecurity/cdk/keycodes'; -import { PortalModule } from '@ptsecurity/cdk/portal'; -import { ScrollingModule, ViewportRuler } from '@ptsecurity/cdk/scrolling'; import { dispatchFakeEvent, dispatchKeyboardEvent diff --git a/packages/mosaic/tabs/tab-header.ts b/packages/mosaic/tabs/tab-header.ts index 00e7157a6..e5bb3cc00 100644 --- a/packages/mosaic/tabs/tab-header.ts +++ b/packages/mosaic/tabs/tab-header.ts @@ -1,3 +1,6 @@ +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { coerceNumberProperty } from '@angular/cdk/coercion'; +import { ViewportRuler } from '@angular/cdk/scrolling'; import { AfterContentChecked, AfterContentInit, @@ -17,10 +20,7 @@ import { ViewEncapsulation } from '@angular/core'; import { FocusKeyManager } from '@ptsecurity/cdk/a11y'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceNumberProperty } from '@ptsecurity/cdk/coercion'; import { END, ENTER, HOME, SPACE } from '@ptsecurity/cdk/keycodes'; -import { ViewportRuler } from '@ptsecurity/cdk/scrolling'; import { merge, of as observableOf, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; diff --git a/packages/mosaic/tabs/tab-label.ts b/packages/mosaic/tabs/tab-label.ts index 9f05c195a..1b7d4a660 100644 --- a/packages/mosaic/tabs/tab-label.ts +++ b/packages/mosaic/tabs/tab-label.ts @@ -1,5 +1,5 @@ +import { CdkPortal } from '@angular/cdk/portal'; import { Directive } from '@angular/core'; -import { CdkPortal } from '@ptsecurity/cdk/portal'; /** Used to flag tab labels for use with the portal directive */ diff --git a/packages/mosaic/tabs/tab-nav-bar/tab-nav-bar.spec.ts b/packages/mosaic/tabs/tab-nav-bar/tab-nav-bar.spec.ts index 75f100b47..8f1a5470c 100644 --- a/packages/mosaic/tabs/tab-nav-bar/tab-nav-bar.spec.ts +++ b/packages/mosaic/tabs/tab-nav-bar/tab-nav-bar.spec.ts @@ -1,4 +1,5 @@ // tslint:disable:no-magic-numbers +import { Direction, Directionality } from '@angular/cdk/bidi'; import { Component, ViewChild, ViewChildren, QueryList } from '@angular/core'; import { async, @@ -6,7 +7,6 @@ import { TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Direction, Directionality } from '@ptsecurity/cdk/bidi'; import { Subject } from 'rxjs'; import { McTabLink, McTabNav, McTabsModule } from '../index'; diff --git a/packages/mosaic/tabs/tab.ts b/packages/mosaic/tabs/tab.ts index a50ac6d4e..cdb41cd28 100644 --- a/packages/mosaic/tabs/tab.ts +++ b/packages/mosaic/tabs/tab.ts @@ -1,3 +1,4 @@ +import { TemplatePortal } from '@angular/cdk/portal'; import { ChangeDetectionStrategy, Component, @@ -12,7 +13,6 @@ import { ViewContainerRef, ViewEncapsulation } from '@angular/core'; -import { TemplatePortal } from '@ptsecurity/cdk/portal'; import { CanDisable, CanDisableCtor, diff --git a/packages/mosaic/tabs/tabs.module.ts b/packages/mosaic/tabs/tabs.module.ts index 15a43cc2f..f9a64fc5f 100644 --- a/packages/mosaic/tabs/tabs.module.ts +++ b/packages/mosaic/tabs/tabs.module.ts @@ -1,7 +1,7 @@ +import { PortalModule } from '@angular/cdk/portal'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { PortalModule } from '@ptsecurity/cdk/portal'; import { McCommonModule } from '@ptsecurity/mosaic/core'; import { McTab } from './tab'; diff --git a/packages/mosaic/tags/tag-input.spec.ts b/packages/mosaic/tags/tag-input.spec.ts index 50065d782..b8cbb3cfc 100644 --- a/packages/mosaic/tags/tag-input.spec.ts +++ b/packages/mosaic/tags/tag-input.spec.ts @@ -1,13 +1,13 @@ // tslint:disable:no-unbound-method // tslint:disable:no-empty +import { Directionality } from '@angular/cdk/bidi'; +import { PlatformModule } from '@angular/cdk/platform'; import { Component, DebugElement, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { ENTER, COMMA } from '@ptsecurity/cdk/keycodes'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { createKeyboardEvent } from '@ptsecurity/cdk/testing'; import { McFormFieldModule } from '@ptsecurity/mosaic/form-field'; import { Subject } from 'rxjs'; diff --git a/packages/mosaic/tags/tag-input.ts b/packages/mosaic/tags/tag-input.ts index 5f5045d32..a322a46ff 100644 --- a/packages/mosaic/tags/tag-input.ts +++ b/packages/mosaic/tags/tag-input.ts @@ -1,5 +1,5 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Directive, ElementRef, EventEmitter, Inject, Input, OnChanges, Output } from '@angular/core'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { hasModifierKey } from '@ptsecurity/cdk/keycodes'; import { MC_TAGS_DEFAULT_OPTIONS, McTagsDefaultOptions } from './tag-default-options'; diff --git a/packages/mosaic/tags/tag-list.component.spec.ts b/packages/mosaic/tags/tag-list.component.spec.ts index ceefc0422..711e2f417 100644 --- a/packages/mosaic/tags/tag-list.component.spec.ts +++ b/packages/mosaic/tags/tag-list.component.spec.ts @@ -2,6 +2,7 @@ // tslint:disable:mocha-no-side-effect-code // tslint:disable:max-func-body-length import { animate, style, transition, trigger } from '@angular/animations'; +import { Directionality, Direction } from '@angular/cdk/bidi'; import { Component, DebugElement, @@ -17,7 +18,6 @@ import { FormControl, FormsModule, NgForm, ReactiveFormsModule, Validators } fro import { By } from '@angular/platform-browser'; import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; import { FocusKeyManager } from '@ptsecurity/cdk/a11y'; -import { Directionality, Direction } from '@ptsecurity/cdk/bidi'; import { BACKSPACE, DELETE, diff --git a/packages/mosaic/tags/tag-list.component.ts b/packages/mosaic/tags/tag-list.component.ts index e5e5abc68..db2669953 100644 --- a/packages/mosaic/tags/tag-list.component.ts +++ b/packages/mosaic/tags/tag-list.component.ts @@ -1,3 +1,6 @@ +import { Directionality } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { SelectionModel } from '@angular/cdk/collections'; import { AfterContentInit, ChangeDetectionStrategy, @@ -18,9 +21,6 @@ import { } from '@angular/core'; import { ControlValueAccessor, FormGroupDirective, NgControl, NgForm } from '@angular/forms'; import { FocusKeyManager } from '@ptsecurity/cdk/a11y'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; -import { SelectionModel } from '@ptsecurity/cdk/collections'; import { BACKSPACE, END, HOME } from '@ptsecurity/cdk/keycodes'; import { CanUpdateErrorState, diff --git a/packages/mosaic/tags/tag.component.spec.ts b/packages/mosaic/tags/tag.component.spec.ts index 06a505897..29cf5a32b 100644 --- a/packages/mosaic/tags/tag.component.spec.ts +++ b/packages/mosaic/tags/tag.component.spec.ts @@ -1,8 +1,8 @@ /* tslint:disable:no-magic-numbers no-empty */ +import { Directionality } from '@angular/cdk/bidi'; import { Component, DebugElement, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { BACKSPACE, DELETE, SPACE } from '@ptsecurity/cdk/keycodes'; import { createKeyboardEvent, dispatchFakeEvent } from '@ptsecurity/cdk/testing'; import { Subject } from 'rxjs'; diff --git a/packages/mosaic/tags/tag.component.ts b/packages/mosaic/tags/tag.component.ts index e0ee10324..68fc003e7 100644 --- a/packages/mosaic/tags/tag.component.ts +++ b/packages/mosaic/tags/tag.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ChangeDetectionStrategy, Component, @@ -15,7 +16,6 @@ import { ViewEncapsulation } from '@angular/core'; import { IFocusableOption } from '@ptsecurity/cdk/a11y'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { BACKSPACE, DELETE, SPACE } from '@ptsecurity/cdk/keycodes'; import { CanColor, diff --git a/packages/mosaic/tags/tag.module.ts b/packages/mosaic/tags/tag.module.ts index 91855db99..a1a525f6d 100644 --- a/packages/mosaic/tags/tag.module.ts +++ b/packages/mosaic/tags/tag.module.ts @@ -1,7 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ENTER } from '@ptsecurity/cdk/keycodes'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { MC_TAGS_DEFAULT_OPTIONS, McTagsDefaultOptions } from './tag-default-options'; import { McTagInput } from './tag-input'; diff --git a/packages/mosaic/textarea/textarea.component.ts b/packages/mosaic/textarea/textarea.component.ts index bf14fe44c..45e74c88f 100644 --- a/packages/mosaic/textarea/textarea.component.ts +++ b/packages/mosaic/textarea/textarea.component.ts @@ -1,10 +1,10 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Directive, DoCheck, ElementRef, Inject, Input, OnChanges, OnDestroy, Optional, Self, InjectionToken, NgZone, OnInit } from '@angular/core'; import { FormGroupDirective, NgControl, NgForm } from '@angular/forms'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { CanUpdateErrorState, CanUpdateErrorStateCtor, @@ -13,7 +13,6 @@ import { } from '@ptsecurity/mosaic/core'; import { McFormFieldControl } from '@ptsecurity/mosaic/form-field'; import { fromEvent, Subscription, Subject } from 'rxjs'; -import { distinctUntilChanged, map } from 'rxjs/operators'; export const MC_TEXTAREA_VALUE_ACCESSOR = diff --git a/packages/mosaic/timepicker/timepicker.module.ts b/packages/mosaic/timepicker/timepicker.module.ts index 1c7f42a89..bfa940978 100644 --- a/packages/mosaic/timepicker/timepicker.module.ts +++ b/packages/mosaic/timepicker/timepicker.module.ts @@ -1,8 +1,8 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { McTimepicker } from './timepicker'; diff --git a/packages/mosaic/timepicker/timepicker.ts b/packages/mosaic/timepicker/timepicker.ts index 444cd1fac..4d8721228 100644 --- a/packages/mosaic/timepicker/timepicker.ts +++ b/packages/mosaic/timepicker/timepicker.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Directive, DoCheck, @@ -19,7 +20,6 @@ import { NgForm, ValidationErrors } from '@angular/forms'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { DateAdapter } from '@ptsecurity/cdk/datetime'; import { CanUpdateErrorState, diff --git a/packages/mosaic/tooltip/tooltip.component.ts b/packages/mosaic/tooltip/tooltip.component.ts index eaa1c9785..92e1a3b57 100644 --- a/packages/mosaic/tooltip/tooltip.component.ts +++ b/packages/mosaic/tooltip/tooltip.component.ts @@ -1,3 +1,19 @@ +import { Directionality } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { + ConnectedOverlayPositionChange, + ConnectionPositionPair, + Overlay, + OverlayRef, + ScrollDispatcher, + ScrollStrategy, + FlexibleConnectedPositionStrategy, + OverlayConnectionPosition, + OriginConnectionPosition, + HorizontalConnectionPos, + VerticalConnectionPos +} from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -18,23 +34,7 @@ import { ViewContainerRef, ViewEncapsulation } from '@angular/core'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; import { ESCAPE } from '@ptsecurity/cdk/keycodes'; -import { - ConnectedOverlayPositionChange, - ConnectionPositionPair, - Overlay, - OverlayRef, - ScrollDispatcher, - IScrollStrategy, - FlexibleConnectedPositionStrategy, - IOverlayConnectionPosition, - IOriginConnectionPosition, - HorizontalConnectionPos, - VerticalConnectionPos -} from '@ptsecurity/cdk/overlay'; -import { ComponentPortal } from '@ptsecurity/cdk/portal'; import { fadeAnimation, DEFAULT_4_POSITIONS, @@ -201,10 +201,10 @@ export class McTooltipComponent { } export const MC_TOOLTIP_SCROLL_STRATEGY = - new InjectionToken<() => IScrollStrategy>('mc-tooltip-scroll-strategy'); + new InjectionToken<() => ScrollStrategy>('mc-tooltip-scroll-strategy'); /** @docs-private */ -export function mcTooltipScrollStrategyFactory(overlay: Overlay): () => IScrollStrategy { +export function mcTooltipScrollStrategyFactory(overlay: Overlay): () => ScrollStrategy { return () => overlay.scrollStrategies.reposition({scrollThrottle: 20}); } @@ -573,10 +573,10 @@ export class McTooltip implements OnInit, OnDestroy { * Returns the origin position and a fallback position based on the user's position preference. * The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`). */ - getOrigin(): {main: IOriginConnectionPosition; fallback: IOriginConnectionPosition} { + getOrigin(): {main: OriginConnectionPosition; fallback: OriginConnectionPosition} { const position = this.mcPlacement; const isLtr = !this.direction || this.direction.value === 'ltr'; - let originPosition: IOriginConnectionPosition; + let originPosition: OriginConnectionPosition; if (position === 'top' || position === 'bottom') { originPosition = {originX: 'center', originY: position === 'top' ? 'top' : 'bottom'}; @@ -603,10 +603,10 @@ export class McTooltip implements OnInit, OnDestroy { } /** Returns the overlay position and a fallback position based on the user's preference */ - getOverlayPosition(): {main: IOverlayConnectionPosition; fallback: IOverlayConnectionPosition} { + getOverlayPosition(): {main: OverlayConnectionPosition; fallback: OverlayConnectionPosition} { const position = this.mcPlacement; const isLtr = !this.direction || this.direction.value === 'ltr'; - let overlayPosition: IOverlayConnectionPosition; + let overlayPosition: OverlayConnectionPosition; if (position === 'top') { overlayPosition = {overlayX: 'center', overlayY: 'bottom'}; diff --git a/packages/mosaic/tooltip/tooltip.module.ts b/packages/mosaic/tooltip/tooltip.module.ts index 913e64634..7855da6ca 100644 --- a/packages/mosaic/tooltip/tooltip.module.ts +++ b/packages/mosaic/tooltip/tooltip.module.ts @@ -1,6 +1,6 @@ +import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; import { McTooltipComponent, diff --git a/packages/mosaic/tooltip/tooltip.spec.ts b/packages/mosaic/tooltip/tooltip.spec.ts index 0d42b4342..322ffb0fc 100644 --- a/packages/mosaic/tooltip/tooltip.spec.ts +++ b/packages/mosaic/tooltip/tooltip.spec.ts @@ -1,7 +1,7 @@ +import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, ElementRef, ViewChild } from '@angular/core'; import { fakeAsync, inject, tick, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { OverlayContainer } from '@ptsecurity/cdk/overlay'; import { dispatchMouseEvent } from '@ptsecurity/cdk/testing'; import { McTooltip } from './tooltip.component'; diff --git a/packages/mosaic/tree-select/tree-select.component.spec.ts b/packages/mosaic/tree-select/tree-select.component.spec.ts index 9548c8d3e..495a37406 100644 --- a/packages/mosaic/tree-select/tree-select.component.spec.ts +++ b/packages/mosaic/tree-select/tree-select.component.spec.ts @@ -6,6 +6,10 @@ /* tslint:disable:prefer-for-of */ // tslint:disable:max-func-body-length +import { Directionality } from '@angular/cdk/bidi'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { Platform } from '@angular/cdk/platform'; +import { ScrollDispatcher, ViewportRuler } from '@angular/cdk/scrolling'; import { ChangeDetectionStrategy, Component, @@ -37,7 +41,6 @@ import { } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Directionality } from '@ptsecurity/cdk/bidi'; import { DOWN_ARROW, END, @@ -50,9 +53,6 @@ import { UP_ARROW, A } from '@ptsecurity/cdk/keycodes'; -import { OverlayContainer } from '@ptsecurity/cdk/overlay'; -import { Platform } from '@ptsecurity/cdk/platform'; -import { ScrollDispatcher, ViewportRuler } from '@ptsecurity/cdk/scrolling'; import { createKeyboardEvent, dispatchEvent, diff --git a/packages/mosaic/tree-select/tree-select.component.ts b/packages/mosaic/tree-select/tree-select.component.ts index 9f785ff29..355fb5776 100644 --- a/packages/mosaic/tree-select/tree-select.component.ts +++ b/packages/mosaic/tree-select/tree-select.component.ts @@ -1,5 +1,12 @@ /* tslint:disable:no-empty */ +import { Directionality } from '@angular/cdk/bidi'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { SelectionModel } from '@angular/cdk/collections'; +import { + CdkConnectedOverlay, + ViewportRuler +} from '@angular/cdk/overlay'; import { AfterContentInit, AfterViewInit, @@ -30,9 +37,6 @@ import { ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, FormGroupDirective, NgControl, NgForm } from '@angular/forms'; -import { Directionality } from '@ptsecurity/cdk/bidi'; -import { coerceBooleanProperty } from '@ptsecurity/cdk/coercion'; -import { SelectionModel } from '@ptsecurity/cdk/collections'; import { DOWN_ARROW, END, @@ -44,10 +48,6 @@ import { UP_ARROW, A, PAGE_UP, PAGE_DOWN } from '@ptsecurity/cdk/keycodes'; -import { - CdkConnectedOverlay, - ViewportRuler -} from '@ptsecurity/cdk/overlay'; import { CdkTree } from '@ptsecurity/cdk/tree'; import { getOptionScrollPosition, diff --git a/packages/mosaic/tree-select/tree-select.module.ts b/packages/mosaic/tree-select/tree-select.module.ts index b5fd13412..bf9102b2d 100644 --- a/packages/mosaic/tree-select/tree-select.module.ts +++ b/packages/mosaic/tree-select/tree-select.module.ts @@ -1,6 +1,6 @@ +import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { OverlayModule } from '@ptsecurity/cdk/overlay'; import { CdkTreeModule } from '@ptsecurity/cdk/tree'; import { MC_SELECT_SCROLL_STRATEGY_PROVIDER, McPseudoCheckboxModule } from '@ptsecurity/mosaic/core'; import { McIconModule } from '@ptsecurity/mosaic/icon'; diff --git a/packages/mosaic/tree/data-source/flat-data-source.ts b/packages/mosaic/tree/data-source/flat-data-source.ts index 7061fd742..ee1f3f05d 100644 --- a/packages/mosaic/tree/data-source/flat-data-source.ts +++ b/packages/mosaic/tree/data-source/flat-data-source.ts @@ -1,4 +1,4 @@ -import { ICollectionViewer, DataSource } from '@ptsecurity/cdk/collections'; +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { FlatTreeControl, ITreeControl } from '@ptsecurity/cdk/tree'; import { BehaviorSubject, merge, Observable } from 'rxjs'; import { map, take } from 'rxjs/operators'; @@ -140,7 +140,7 @@ export class McTreeFlatDataSource extends DataSource { this._data = new BehaviorSubject(initialData); } - connect(collectionViewer: ICollectionViewer): Observable { + connect(collectionViewer: CollectionViewer): Observable { const changes = [ collectionViewer.viewChange, this.treeControl.expansionModel.changed, diff --git a/packages/mosaic/tree/data-source/nested-data-source.ts b/packages/mosaic/tree/data-source/nested-data-source.ts index 8d31b35e1..c6836471b 100644 --- a/packages/mosaic/tree/data-source/nested-data-source.ts +++ b/packages/mosaic/tree/data-source/nested-data-source.ts @@ -1,4 +1,4 @@ -import { ICollectionViewer, DataSource } from '@ptsecurity/cdk/collections'; +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { BehaviorSubject, merge, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -22,7 +22,7 @@ export class McTreeNestedDataSource extends DataSource { /* tslint:disable-next-line:naming-convention */ private _data = new BehaviorSubject([]); - connect(collectionViewer: ICollectionViewer): Observable { + connect(collectionViewer: CollectionViewer): Observable { return merge(...[collectionViewer.viewChange, this._data]) .pipe(map(() => this.data)); } diff --git a/packages/mosaic/tree/tree-option.ts b/packages/mosaic/tree/tree-option.ts index a70c780b3..0153f2d5d 100644 --- a/packages/mosaic/tree/tree-option.ts +++ b/packages/mosaic/tree/tree-option.ts @@ -1,3 +1,4 @@ +import { SelectionModel } from '@angular/cdk/collections'; import { ChangeDetectorRef, Component, @@ -7,9 +8,8 @@ import { ElementRef, Inject, Optional, - InjectionToken, ChangeDetectionStrategy, ViewEncapsulation, OnInit + InjectionToken, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core'; -import { SelectionModel } from '@ptsecurity/cdk/collections'; import { CdkTreeNode } from '@ptsecurity/cdk/tree'; import { CanDisable, toBoolean } from '@ptsecurity/mosaic/core'; diff --git a/packages/mosaic/tree/tree-selection.ts b/packages/mosaic/tree/tree-selection.ts index cf843ff10..ef77e1c68 100644 --- a/packages/mosaic/tree/tree-selection.ts +++ b/packages/mosaic/tree/tree-selection.ts @@ -1,3 +1,4 @@ +import { SelectionModel } from '@angular/cdk/collections'; import { AfterContentInit, Attribute, @@ -20,7 +21,6 @@ import { import { NodeDef, ViewData } from '@angular/core/esm2015/src/view'; import { ControlValueAccessor, NgControl } from '@angular/forms'; import { ActiveDescendantKeyManager } from '@ptsecurity/cdk/a11y'; -import { SelectionModel } from '@ptsecurity/cdk/collections'; import { END, ENTER, HOME, LEFT_ARROW, PAGE_DOWN, PAGE_UP, RIGHT_ARROW, SPACE } from '@ptsecurity/cdk/keycodes'; import { CdkTree, CdkTreeNodeOutlet } from '@ptsecurity/cdk/tree'; import { diff --git a/packages/mosaic/vertical-navbar/vertical-navbar.module.ts b/packages/mosaic/vertical-navbar/vertical-navbar.module.ts index eeba50e0f..9b031a353 100644 --- a/packages/mosaic/vertical-navbar/vertical-navbar.module.ts +++ b/packages/mosaic/vertical-navbar/vertical-navbar.module.ts @@ -1,7 +1,7 @@ +import { PlatformModule } from '@angular/cdk/platform'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { A11yModule } from '@ptsecurity/cdk/a11y'; -import { PlatformModule } from '@ptsecurity/cdk/platform'; import { McIconModule } from '@ptsecurity/mosaic/icon'; import { diff --git a/tests/karma-system-config.js b/tests/karma-system-config.js index 14d941c7d..2d262842c 100644 --- a/tests/karma-system-config.js +++ b/tests/karma-system-config.js @@ -10,6 +10,17 @@ System.config({ 'moment': 'node:moment/min/moment-with-locales.min.js', 'messageformat': 'node:messageformat/messageformat.js', + '@angular/cdk/a11y': 'node:@angular/cdk/bundles/cdk-a11y.umd.js', + '@angular/cdk/bidi': 'node:@angular/cdk/bundles/cdk-bidi.umd.js', + '@angular/cdk/coercion': 'node:@angular/cdk/bundles/cdk-coercion.umd.js', + '@angular/cdk/collections': 'node:@angular/cdk/bundles/cdk-collections.umd.js', + '@angular/cdk/keycodes': 'node:@angular/cdk/bundles/cdk-keycodes.umd.js', + '@angular/cdk/platform': 'node:@angular/cdk/bundles/cdk-platform.umd.js', + '@angular/cdk/portal': 'node:@angular/cdk/bundles/cdk-portal.umd.js', + '@angular/cdk/observers': 'node:@angular/cdk/bundles/cdk-observers.umd.js', + '@angular/cdk/overlay': 'node:@angular/cdk/bundles/cdk-overlay.umd.js', + '@angular/cdk/scrolling': 'node:@angular/cdk/bundles/cdk-scrolling.umd.js', + // Angular specific mappings. '@angular/core': 'node:@angular/core/bundles/core.umd.js', '@angular/core/testing': 'node:@angular/core/bundles/core-testing.umd.js', @@ -39,17 +50,8 @@ System.config({ '@ptsecurity/cdk': 'dist/packages/cdk/index.js', '@ptsecurity/cdk/a11y': 'dist/packages/cdk/a11y/index.js', - '@ptsecurity/cdk/accordion': 'dist/packages/cdk/accordion/index.js', - '@ptsecurity/cdk/bidi': 'dist/packages/cdk/bidi/index.js', '@ptsecurity/cdk/datetime': 'dist/packages/cdk/datetime/index.js', - '@ptsecurity/cdk/coercion': 'dist/packages/cdk/coercion/index.js', - '@ptsecurity/cdk/collections': 'dist/packages/cdk/collections/index.js', '@ptsecurity/cdk/keycodes': 'dist/packages/cdk/keycodes/index.js', - '@ptsecurity/cdk/layout': 'dist/packages/cdk/layout/index.js', - '@ptsecurity/cdk/overlay': 'dist/packages/cdk/overlay/index.js', - '@ptsecurity/cdk/platform': 'dist/packages/cdk/platform/index.js', - '@ptsecurity/cdk/portal': 'dist/packages/cdk/portal/index.js', - '@ptsecurity/cdk/scrolling': 'dist/packages/cdk/scrolling/index.js', '@ptsecurity/cdk/testing': 'dist/packages/cdk/testing/index.js', '@ptsecurity/cdk/tree': 'dist/packages/cdk/tree/index.js', diff --git a/tools/packages/rollup-globals.ts b/tools/packages/rollup-globals.ts index 7c50aeec0..63cdb84a0 100644 --- a/tools/packages/rollup-globals.ts +++ b/tools/packages/rollup-globals.ts @@ -8,6 +8,7 @@ import { dashCaseToCamelCase } from './utils'; /** Generates rollup entry point mappings for the given package and entry points. */ function generateRollupEntryPoints(packageName: string, entryPoints: string[]): {[k: string]: string} { +// tslint:disable-next-line: no-inferred-empty-object-type return entryPoints.reduce((globals: {[k: string]: string}, entryPoint: string) => { globals[`@ptsecurity/${packageName}/${entryPoint}`] = `ng.${dashCaseToCamelCase(packageName)}.${dashCaseToCamelCase(entryPoint)}`; @@ -38,6 +39,17 @@ export const rollupGlobals = { /* tslint:disable-next-line:object-literal-key-quotes */ 'messageformat': 'messageformat', + '@angular/cdk': 'ng.cdk', + '@angular/cdk/a11y': 'ng.cdk.a11y', + '@angular/cdk/bidi': 'ng.cdk.bidi', + '@angular/cdk/coercion': 'ng.cdk.coercion', + '@angular/cdk/collections': 'ng.cdk.collections', + '@angular/cdk/platform': 'ng.cdk.platform', + '@angular/cdk/portal': 'ng.cdk.portal', + '@angular/cdk/observers': 'ng.cdk.observers', + '@angular/cdk/overlay': 'ng.cdk.overlay', + '@angular/cdk/scrolling': 'ng.cdk.scrolling', + '@angular/animations': 'ng.animations', '@angular/common': 'ng.common', '@angular/common/http': 'ng.common.http', @@ -53,7 +65,7 @@ export const rollupGlobals = { '@angular/platform-server': 'ng.platformServer', '@angular/router': 'ng.router', - '@ptsecurity/cdk': 'ng.cdk', + '@ptsecurity/cdk': 'ng.mosaic.cdk', '@ptsecurity/mosaic': 'ng.mosaic', '@ptsecurity/mosaic-examples': 'ng.mosaicExamples', '@ptsecurity/mosaic-moment-adapter': 'ng.mosaicMomentAdapter', diff --git a/yarn.lock b/yarn.lock index 01445c10c..bbd14cfcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,6 +28,15 @@ dependencies: tslib "^1.9.0" +"@angular/cdk@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-8.0.1.tgz#106872aa7fac0c55144ed5dd60df9af7e24b436a" + integrity sha512-Ul7rVU/rr4qGHs1w24P/6MHEosYp8AHRxBHrp/yJ50eHbHVS31FyfO8gKfNqPc1bnL1YoYYCs0hIBwNDaz8uDg== + dependencies: + tslib "^1.7.1" + optionalDependencies: + parse5 "^5.0.0" + "@angular/common@^8.0.0": version "8.0.2" resolved "https://registry.yarnpkg.com/@angular/common/-/common-8.0.2.tgz#826ff5942a52a26b865a236afcab572d753e21c2" @@ -8310,7 +8319,7 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= -parse5@5.1.0: +parse5@5.1.0, parse5@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== @@ -11184,7 +11193,7 @@ tslib@1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==