| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844 |
- /**
- * @license
- * Copyright Google LLC All Rights Reserved.
- *
- * Use of this source code is governed by an MIT-style license that can be
- * found in the LICENSE file at https://angular.io/license
- */
- import { coerceCssPixelValue, coerceArray, coerceBooleanProperty } from '@angular/cdk/coercion';
- import { ScrollDispatcher, ViewportRuler, ScrollingModule, VIEWPORT_RULER_PROVIDER } from '@angular/cdk/scrolling';
- export { ViewportRuler, VIEWPORT_RULER_PROVIDER, CdkScrollable, ScrollDispatcher } from '@angular/cdk/scrolling';
- import { DOCUMENT, Location } from '@angular/common';
- import { Inject, Injectable, NgZone, Optional, NgModule, SkipSelf, ApplicationRef, ComponentFactoryResolver, Injector, ElementRef, Directive, EventEmitter, InjectionToken, Input, Output, TemplateRef, ViewContainerRef, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
- import { Observable, Subject, merge, Subscription } from 'rxjs';
- import { take, takeUntil } from 'rxjs/operators';
- import { Platform } from '@angular/cdk/platform';
- import { Directionality, BidiModule } from '@angular/cdk/bidi';
- import { DomPortalOutlet, TemplatePortal, PortalModule } from '@angular/cdk/portal';
- import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Strategy that will prevent the user from scrolling while the overlay is visible.
- */
- class BlockScrollStrategy {
- /**
- * @param {?} _viewportRuler
- * @param {?} document
- */
- constructor(_viewportRuler, document) {
- this._viewportRuler = _viewportRuler;
- this._previousHTMLStyles = { top: '', left: '' };
- this._isEnabled = false;
- this._document = document;
- }
- /**
- * Attaches this scroll strategy to an overlay.
- * @return {?}
- */
- attach() { }
- /**
- * Blocks page-level scroll while the attached overlay is open.
- * @return {?}
- */
- enable() {
- if (this._canBeEnabled()) {
- /** @type {?} */
- const root = (/** @type {?} */ (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.
- * @return {?}
- */
- disable() {
- if (this._isEnabled) {
- /** @type {?} */
- const html = (/** @type {?} */ (this._document.documentElement));
- /** @type {?} */
- const body = (/** @type {?} */ (this._document.body));
- /** @type {?} */
- const htmlStyle = (/** @type {?} */ (html.style));
- /** @type {?} */
- const bodyStyle = (/** @type {?} */ (body.style));
- /** @type {?} */
- const previousHtmlScrollBehavior = htmlStyle.scrollBehavior || '';
- /** @type {?} */
- const previousBodyScrollBehavior = bodyStyle.scrollBehavior || '';
- this._isEnabled = false;
- htmlStyle.left = this._previousHTMLStyles.left;
- htmlStyle.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
- htmlStyle.scrollBehavior = bodyStyle.scrollBehavior = 'auto';
- window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top);
- htmlStyle.scrollBehavior = previousHtmlScrollBehavior;
- bodyStyle.scrollBehavior = previousBodyScrollBehavior;
- }
- }
- /**
- * @private
- * @return {?}
- */
- _canBeEnabled() {
- // 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.
- /** @type {?} */
- const html = (/** @type {?} */ (this._document.documentElement));
- if (html.classList.contains('cdk-global-scrollblock') || this._isEnabled) {
- return false;
- }
- /** @type {?} */
- const body = this._document.body;
- /** @type {?} */
- const viewport = this._viewportRuler.getViewportSize();
- return body.scrollHeight > viewport.height || body.scrollWidth > viewport.width;
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Returns an error to be thrown when attempting to attach an already-attached scroll strategy.
- * @return {?}
- */
- function getMatScrollStrategyAlreadyAttachedError() {
- return Error(`Scroll strategy has already been attached.`);
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Strategy that will close the overlay as soon as the user starts scrolling.
- */
- class CloseScrollStrategy {
- /**
- * @param {?} _scrollDispatcher
- * @param {?} _ngZone
- * @param {?} _viewportRuler
- * @param {?=} _config
- */
- constructor(_scrollDispatcher, _ngZone, _viewportRuler, _config) {
- this._scrollDispatcher = _scrollDispatcher;
- this._ngZone = _ngZone;
- this._viewportRuler = _viewportRuler;
- this._config = _config;
- this._scrollSubscription = null;
- /**
- * Detaches the overlay ref and disables the scroll strategy.
- */
- this._detach = (/**
- * @return {?}
- */
- () => {
- this.disable();
- if (this._overlayRef.hasAttached()) {
- this._ngZone.run((/**
- * @return {?}
- */
- () => this._overlayRef.detach()));
- }
- });
- }
- /**
- * Attaches this scroll strategy to an overlay.
- * @param {?} overlayRef
- * @return {?}
- */
- attach(overlayRef) {
- if (this._overlayRef) {
- throw getMatScrollStrategyAlreadyAttachedError();
- }
- this._overlayRef = overlayRef;
- }
- /**
- * Enables the closing of the attached overlay on scroll.
- * @return {?}
- */
- enable() {
- if (this._scrollSubscription) {
- return;
- }
- /** @type {?} */
- 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((/**
- * @return {?}
- */
- () => {
- /** @type {?} */
- const scrollPosition = this._viewportRuler.getViewportScrollPosition().top;
- if (Math.abs(scrollPosition - this._initialScrollPosition) > (/** @type {?} */ ((/** @type {?} */ (this._config)).threshold))) {
- this._detach();
- }
- else {
- this._overlayRef.updatePosition();
- }
- }));
- }
- else {
- this._scrollSubscription = stream.subscribe(this._detach);
- }
- }
- /**
- * Disables the closing the attached overlay on scroll.
- * @return {?}
- */
- disable() {
- if (this._scrollSubscription) {
- this._scrollSubscription.unsubscribe();
- this._scrollSubscription = null;
- }
- }
- /**
- * @return {?}
- */
- detach() {
- this.disable();
- this._overlayRef = (/** @type {?} */ (null));
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Scroll strategy that doesn't do anything.
- */
- class NoopScrollStrategy {
- /**
- * Does nothing, as this scroll strategy is a no-op.
- * @return {?}
- */
- enable() { }
- /**
- * Does nothing, as this scroll strategy is a no-op.
- * @return {?}
- */
- disable() { }
- /**
- * Does nothing, as this scroll strategy is a no-op.
- * @return {?}
- */
- attach() { }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- // TODO(jelbourn): move this to live with the rest of the scrolling code
- // TODO(jelbourn): someday replace this with IntersectionObservers
- /**
- * Gets whether an element is scrolled outside of view by any of its parent scrolling containers.
- * \@docs-private
- * @param {?} element Dimensions of the element (from getBoundingClientRect)
- * @param {?} scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
- * @return {?} Whether the element is scrolled out of view
- */
- function isElementScrolledOutsideView(element, scrollContainers) {
- return scrollContainers.some((/**
- * @param {?} containerBounds
- * @return {?}
- */
- containerBounds => {
- /** @type {?} */
- const outsideAbove = element.bottom < containerBounds.top;
- /** @type {?} */
- const outsideBelow = element.top > containerBounds.bottom;
- /** @type {?} */
- const outsideLeft = element.right < containerBounds.left;
- /** @type {?} */
- const outsideRight = element.left > containerBounds.right;
- return outsideAbove || outsideBelow || outsideLeft || outsideRight;
- }));
- }
- /**
- * Gets whether an element is clipped by any of its scrolling containers.
- * \@docs-private
- * @param {?} element Dimensions of the element (from getBoundingClientRect)
- * @param {?} scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
- * @return {?} Whether the element is clipped
- */
- function isElementClippedByScrolling(element, scrollContainers) {
- return scrollContainers.some((/**
- * @param {?} scrollContainerRect
- * @return {?}
- */
- scrollContainerRect => {
- /** @type {?} */
- const clippedAbove = element.top < scrollContainerRect.top;
- /** @type {?} */
- const clippedBelow = element.bottom > scrollContainerRect.bottom;
- /** @type {?} */
- const clippedLeft = element.left < scrollContainerRect.left;
- /** @type {?} */
- const clippedRight = element.right > scrollContainerRect.right;
- return clippedAbove || clippedBelow || clippedLeft || clippedRight;
- }));
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Strategy that will update the element position as the user is scrolling.
- */
- class RepositionScrollStrategy {
- /**
- * @param {?} _scrollDispatcher
- * @param {?} _viewportRuler
- * @param {?} _ngZone
- * @param {?=} _config
- */
- constructor(_scrollDispatcher, _viewportRuler, _ngZone, _config) {
- this._scrollDispatcher = _scrollDispatcher;
- this._viewportRuler = _viewportRuler;
- this._ngZone = _ngZone;
- this._config = _config;
- this._scrollSubscription = null;
- }
- /**
- * Attaches this scroll strategy to an overlay.
- * @param {?} overlayRef
- * @return {?}
- */
- attach(overlayRef) {
- if (this._overlayRef) {
- throw getMatScrollStrategyAlreadyAttachedError();
- }
- this._overlayRef = overlayRef;
- }
- /**
- * Enables repositioning of the attached overlay on scroll.
- * @return {?}
- */
- enable() {
- if (!this._scrollSubscription) {
- /** @type {?} */
- const throttle = this._config ? this._config.scrollThrottle : 0;
- this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe((/**
- * @return {?}
- */
- () => {
- this._overlayRef.updatePosition();
- // TODO(crisbeto): make `close` on by default once all components can handle it.
- if (this._config && this._config.autoClose) {
- /** @type {?} */
- 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.
- /** @type {?} */
- const parentRects = [{ width, height, bottom: height, right: width, top: 0, left: 0 }];
- if (isElementScrolledOutsideView(overlayRect, parentRects)) {
- this.disable();
- this._ngZone.run((/**
- * @return {?}
- */
- () => this._overlayRef.detach()));
- }
- }
- }));
- }
- }
- /**
- * Disables repositioning of the attached overlay on scroll.
- * @return {?}
- */
- disable() {
- if (this._scrollSubscription) {
- this._scrollSubscription.unsubscribe();
- this._scrollSubscription = null;
- }
- }
- /**
- * @return {?}
- */
- detach() {
- this.disable();
- this._overlayRef = (/** @type {?} */ (null));
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * 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.
- */
- class ScrollStrategyOptions {
- /**
- * @param {?} _scrollDispatcher
- * @param {?} _viewportRuler
- * @param {?} _ngZone
- * @param {?} document
- */
- constructor(_scrollDispatcher, _viewportRuler, _ngZone, document) {
- this._scrollDispatcher = _scrollDispatcher;
- this._viewportRuler = _viewportRuler;
- this._ngZone = _ngZone;
- /**
- * Do nothing on scroll.
- */
- this.noop = (/**
- * @return {?}
- */
- () => new NoopScrollStrategy());
- /**
- * Close the overlay as soon as the user scrolls.
- * @param config Configuration to be used inside the scroll strategy.
- */
- this.close = (/**
- * @param {?=} config
- * @return {?}
- */
- (config) => new CloseScrollStrategy(this._scrollDispatcher, this._ngZone, this._viewportRuler, config));
- /**
- * Block scrolling.
- */
- this.block = (/**
- * @return {?}
- */
- () => 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.
- */
- this.reposition = (/**
- * @param {?=} config
- * @return {?}
- */
- (config) => new RepositionScrollStrategy(this._scrollDispatcher, this._viewportRuler, this._ngZone, config));
- this._document = document;
- }
- }
- ScrollStrategyOptions.decorators = [
- { type: Injectable, args: [{ providedIn: 'root' },] },
- ];
- /** @nocollapse */
- ScrollStrategyOptions.ctorParameters = () => [
- { type: ScrollDispatcher },
- { type: ViewportRuler },
- { type: NgZone },
- { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
- ];
- /** @nocollapse */ ScrollStrategyOptions.ngInjectableDef = ɵɵdefineInjectable({ factory: function ScrollStrategyOptions_Factory() { return new ScrollStrategyOptions(ɵɵinject(ScrollDispatcher), ɵɵinject(ViewportRuler), ɵɵinject(NgZone), ɵɵinject(DOCUMENT)); }, token: ScrollStrategyOptions, providedIn: "root" });
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Initial configuration used when creating an overlay.
- */
- class OverlayConfig {
- /**
- * @param {?=} config
- */
- constructor(config) {
- /**
- * Strategy to be used when handling scroll events while the overlay is open.
- */
- this.scrollStrategy = new NoopScrollStrategy();
- /**
- * Custom class to add to the overlay pane.
- */
- this.panelClass = '';
- /**
- * Whether the overlay has a backdrop.
- */
- this.hasBackdrop = false;
- /**
- * Custom class to add to the backdrop
- */
- this.backdropClass = 'cdk-overlay-dark-backdrop';
- /**
- * Whether the overlay should be disposed of when the user goes backwards/forwards in history.
- * Note that this usually doesn't include clicking on links (unless the user is using
- * the `HashLocationStrategy`).
- */
- this.disposeOnNavigation = false;
- if (config) {
- /** @type {?} */
- const configKeys = (/** @type {?} */ (Object.keys(config)));
- for (const key of configKeys) {
- if (config[key] !== undefined) {
- // TypeScript, as of version 3.5, sees the left-hand-side of this expression
- // as "I don't know *which* key this is, so the only valid value is the intersection
- // of all the posible values." In this case, that happens to be `undefined`. TypeScript
- // is not smart enough to see that the right-hand-side is actually an access of the same
- // exact type with the same exact key, meaning that the value type must be identical.
- // So we use `any` to work around this.
- this[key] = (/** @type {?} */ (config[key]));
- }
- }
- }
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * The points of the origin element and the overlay element to connect.
- */
- class ConnectionPositionPair {
- /**
- * @param {?} origin
- * @param {?} overlay
- * @param {?=} offsetX
- * @param {?=} offsetY
- * @param {?=} panelClass
- */
- constructor(origin, overlay, offsetX, offsetY, panelClass) {
- this.offsetX = offsetX;
- this.offsetY = offsetY;
- this.panelClass = panelClass;
- 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
- */
- class ScrollingVisibility {
- }
- /**
- * The change event emitted by the strategy when a fallback position is used.
- */
- class ConnectedOverlayPositionChange {
- /**
- * @param {?} connectionPair
- * @param {?} scrollableViewProperties
- */
- constructor(connectionPair, scrollableViewProperties) {
- this.connectionPair = connectionPair;
- this.scrollableViewProperties = scrollableViewProperties;
- }
- }
- /** @nocollapse */
- ConnectedOverlayPositionChange.ctorParameters = () => [
- { type: ConnectionPositionPair },
- { type: ScrollingVisibility, decorators: [{ type: Optional }] }
- ];
- /**
- * Validates whether a vertical position property matches the expected values.
- * \@docs-private
- * @param {?} property Name of the property being validated.
- * @param {?} value Value of the property being validated.
- * @return {?}
- */
- function validateVerticalPosition(property, value) {
- 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.
- * \@docs-private
- * @param {?} property Name of the property being validated.
- * @param {?} value Value of the property being validated.
- * @return {?}
- */
- function validateHorizontalPosition(property, value) {
- if (value !== 'start' && value !== 'end' && value !== 'center') {
- throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` +
- `Expected "start", "end" or "center".`);
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * 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.
- */
- class OverlayKeyboardDispatcher {
- /**
- * @param {?} document
- */
- constructor(document) {
- /**
- * Currently attached overlays in the order they were attached.
- */
- this._attachedOverlays = [];
- /**
- * Keyboard event listener that will be attached to the body.
- */
- this._keydownListener = (/**
- * @param {?} event
- * @return {?}
- */
- (event) => {
- /** @type {?} */
- const overlays = this._attachedOverlays;
- for (let i = overlays.length - 1; i > -1; i--) {
- // Dispatch the keydown event to the top overlay which has subscribers to its keydown events.
- // 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). We skip overlays without keydown event subscriptions,
- // because we don't want overlays that don't handle keyboard events to block the ones below
- // them that do.
- if (overlays[i]._keydownEventSubscriptions > 0) {
- overlays[i]._keydownEvents.next(event);
- break;
- }
- }
- });
- this._document = document;
- }
- /**
- * @return {?}
- */
- ngOnDestroy() {
- this._detach();
- }
- /**
- * Add a new overlay to the list of attached overlay refs.
- * @param {?} overlayRef
- * @return {?}
- */
- add(overlayRef) {
- // Ensure that we don't get the same overlay multiple times.
- this.remove(overlayRef);
- // 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.
- * @param {?} overlayRef
- * @return {?}
- */
- remove(overlayRef) {
- /** @type {?} */
- 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
- * @return {?}
- */
- _detach() {
- if (this._isAttached) {
- this._document.body.removeEventListener('keydown', this._keydownListener);
- this._isAttached = false;
- }
- }
- }
- OverlayKeyboardDispatcher.decorators = [
- { type: Injectable, args: [{ providedIn: 'root' },] },
- ];
- /** @nocollapse */
- OverlayKeyboardDispatcher.ctorParameters = () => [
- { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
- ];
- /** @nocollapse */ OverlayKeyboardDispatcher.ngInjectableDef = ɵɵdefineInjectable({ factory: function OverlayKeyboardDispatcher_Factory() { return new OverlayKeyboardDispatcher(ɵɵinject(DOCUMENT)); }, token: OverlayKeyboardDispatcher, providedIn: "root" });
- /**
- * \@docs-private \@deprecated \@breaking-change 8.0.0
- * @param {?} dispatcher
- * @param {?} _document
- * @return {?}
- */
- function OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY(dispatcher, _document) {
- return dispatcher || new OverlayKeyboardDispatcher(_document);
- }
- /**
- * \@docs-private \@deprecated \@breaking-change 8.0.0
- * @type {?}
- */
- 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],
- (/** @type {?} */ (
- // Coerce to `InjectionToken` so that the `deps` match the "shape"
- // of the type expected by Angular
- DOCUMENT))
- ],
- useFactory: OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY
- };
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Container inside which all overlays will render.
- */
- class OverlayContainer {
- /**
- * @param {?} document
- */
- constructor(document) {
- this._document = document;
- }
- /**
- * @return {?}
- */
- 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.
- * @return {?} the container element
- */
- getContainerElement() {
- 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
- * @return {?}
- */
- _createContainer() {
- /** @type {?} */
- const containerClass = 'cdk-overlay-container';
- /** @type {?} */
- const previousContainers = this._document.getElementsByClassName(containerClass);
- // Remove any old containers. This can happen when transitioning from the server to the client.
- for (let i = 0; i < previousContainers.length; i++) {
- (/** @type {?} */ (previousContainers[i].parentNode)).removeChild(previousContainers[i]);
- }
- /** @type {?} */
- const container = this._document.createElement('div');
- container.classList.add(containerClass);
- this._document.body.appendChild(container);
- this._containerElement = container;
- }
- }
- OverlayContainer.decorators = [
- { type: Injectable, args: [{ providedIn: 'root' },] },
- ];
- /** @nocollapse */
- OverlayContainer.ctorParameters = () => [
- { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
- ];
- /** @nocollapse */ OverlayContainer.ngInjectableDef = ɵɵdefineInjectable({ factory: function OverlayContainer_Factory() { return new OverlayContainer(ɵɵinject(DOCUMENT)); }, token: OverlayContainer, providedIn: "root" });
- /**
- * \@docs-private \@deprecated \@breaking-change 8.0.0
- * @param {?} parentContainer
- * @param {?} _document
- * @return {?}
- */
- function OVERLAY_CONTAINER_PROVIDER_FACTORY(parentContainer, _document) {
- return parentContainer || new OverlayContainer(_document);
- }
- /**
- * \@docs-private \@deprecated \@breaking-change 8.0.0
- * @type {?}
- */
- 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],
- (/** @type {?} */ (DOCUMENT))
- ],
- useFactory: OVERLAY_CONTAINER_PROVIDER_FACTORY
- };
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Reference to an overlay that has been created with the Overlay service.
- * Used to manipulate or dispose of said overlay.
- */
- class OverlayRef {
- /**
- * @param {?} _portalOutlet
- * @param {?} _host
- * @param {?} _pane
- * @param {?} _config
- * @param {?} _ngZone
- * @param {?} _keyboardDispatcher
- * @param {?} _document
- * @param {?=} _location
- */
- constructor(_portalOutlet, _host, _pane, _config, _ngZone, _keyboardDispatcher, _document, _location) {
- this._portalOutlet = _portalOutlet;
- this._host = _host;
- this._pane = _pane;
- this._config = _config;
- this._ngZone = _ngZone;
- this._keyboardDispatcher = _keyboardDispatcher;
- this._document = _document;
- this._location = _location;
- this._backdropElement = null;
- this._backdropClick = new Subject();
- this._attachments = new Subject();
- this._detachments = new Subject();
- this._locationChanges = Subscription.EMPTY;
- this._backdropClickHandler = (/**
- * @param {?} event
- * @return {?}
- */
- (event) => this._backdropClick.next(event));
- this._keydownEventsObservable = new Observable((/**
- * @param {?} observer
- * @return {?}
- */
- (observer) => {
- /** @type {?} */
- const subscription = this._keydownEvents.subscribe(observer);
- this._keydownEventSubscriptions++;
- return (/**
- * @return {?}
- */
- () => {
- subscription.unsubscribe();
- this._keydownEventSubscriptions--;
- });
- }));
- /**
- * Stream of keydown events dispatched to this overlay.
- */
- this._keydownEvents = new Subject();
- /**
- * Amount of subscriptions to the keydown events.
- */
- this._keydownEventSubscriptions = 0;
- if (_config.scrollStrategy) {
- this._scrollStrategy = _config.scrollStrategy;
- this._scrollStrategy.attach(this);
- }
- this._positionStrategy = _config.positionStrategy;
- }
- /**
- * The overlay's HTML element
- * @return {?}
- */
- get overlayElement() {
- return this._pane;
- }
- /**
- * The overlay's backdrop HTML element.
- * @return {?}
- */
- get backdropElement() {
- 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.
- * @return {?}
- */
- get hostElement() {
- return this._host;
- }
- /**
- * 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.
- * @return {?} The portal attachment result.
- */
- attach(portal) {
- /** @type {?} */
- let attachResult = this._portalOutlet.attach(portal);
- if (this._positionStrategy) {
- this._positionStrategy.attach(this);
- }
- // Update the pane element with the given configuration.
- if (!this._host.parentElement && this._previousHostParent) {
- this._previousHostParent.appendChild(this._host);
- }
- this._updateStackingOrder();
- this._updateElementSize();
- this._updateElementDirection();
- if (this._scrollStrategy) {
- this._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((/**
- * @return {?}
- */
- () => {
- // 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);
- // @breaking-change 8.0.0 remove the null check for `_location`
- // once the constructor parameter is made required.
- if (this._config.disposeOnNavigation && this._location) {
- this._locationChanges = this._location.subscribe((/**
- * @return {?}
- */
- () => this.dispose()));
- }
- return attachResult;
- }
- /**
- * Detaches an overlay from a portal.
- * @return {?} The portal detachment result.
- */
- detach() {
- 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._positionStrategy && this._positionStrategy.detach) {
- this._positionStrategy.detach();
- }
- if (this._scrollStrategy) {
- this._scrollStrategy.disable();
- }
- /** @type {?} */
- 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);
- // Keeping the host element in DOM the can cause scroll jank, because it still gets
- // rendered, even though it's transparent and unclickable which is why we remove it.
- this._detachContentWhenStable();
- // Stop listening for location changes.
- this._locationChanges.unsubscribe();
- return detachmentResult;
- }
- /**
- * Cleans up the overlay from the DOM.
- * @return {?}
- */
- dispose() {
- /** @type {?} */
- const isAttached = this.hasAttached();
- if (this._positionStrategy) {
- this._positionStrategy.dispose();
- }
- this._disposeScrollStrategy();
- this.detachBackdrop();
- this._locationChanges.unsubscribe();
- 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 = (/** @type {?} */ (null));
- }
- this._previousHostParent = this._pane = (/** @type {?} */ (null));
- if (isAttached) {
- this._detachments.next();
- }
- this._detachments.complete();
- }
- /**
- * Whether the overlay has attached content.
- * @return {?}
- */
- hasAttached() {
- return this._portalOutlet.hasAttached();
- }
- /**
- * Gets an observable that emits when the backdrop has been clicked.
- * @return {?}
- */
- backdropClick() {
- return this._backdropClick.asObservable();
- }
- /**
- * Gets an observable that emits when the overlay has been attached.
- * @return {?}
- */
- attachments() {
- return this._attachments.asObservable();
- }
- /**
- * Gets an observable that emits when the overlay has been detached.
- * @return {?}
- */
- detachments() {
- return this._detachments.asObservable();
- }
- /**
- * Gets an observable of keydown events targeted to this overlay.
- * @return {?}
- */
- keydownEvents() {
- return this._keydownEventsObservable;
- }
- /**
- * Gets the current overlay configuration, which is immutable.
- * @return {?}
- */
- getConfig() {
- return this._config;
- }
- /**
- * Updates the position of the overlay based on the position strategy.
- * @return {?}
- */
- updatePosition() {
- if (this._positionStrategy) {
- this._positionStrategy.apply();
- }
- }
- /**
- * Switches to a new position strategy and updates the overlay position.
- * @param {?} strategy
- * @return {?}
- */
- updatePositionStrategy(strategy) {
- if (strategy === this._positionStrategy) {
- return;
- }
- if (this._positionStrategy) {
- this._positionStrategy.dispose();
- }
- this._positionStrategy = strategy;
- if (this.hasAttached()) {
- strategy.attach(this);
- this.updatePosition();
- }
- }
- /**
- * Update the size properties of the overlay.
- * @param {?} sizeConfig
- * @return {?}
- */
- updateSize(sizeConfig) {
- this._config = Object.assign({}, this._config, sizeConfig);
- this._updateElementSize();
- }
- /**
- * Sets the LTR/RTL direction for the overlay.
- * @param {?} dir
- * @return {?}
- */
- setDirection(dir) {
- this._config = Object.assign({}, this._config, { direction: dir });
- this._updateElementDirection();
- }
- /**
- * Add a CSS class or an array of classes to the overlay pane.
- * @param {?} classes
- * @return {?}
- */
- addPanelClass(classes) {
- if (this._pane) {
- this._toggleClasses(this._pane, classes, true);
- }
- }
- /**
- * Remove a CSS class or an array of classes from the overlay pane.
- * @param {?} classes
- * @return {?}
- */
- removePanelClass(classes) {
- if (this._pane) {
- this._toggleClasses(this._pane, classes, false);
- }
- }
- /**
- * Returns the layout direction of the overlay panel.
- * @return {?}
- */
- getDirection() {
- /** @type {?} */
- const direction = this._config.direction;
- if (!direction) {
- return 'ltr';
- }
- return typeof direction === 'string' ? direction : direction.value;
- }
- /**
- * Switches to a new scroll strategy.
- * @param {?} strategy
- * @return {?}
- */
- updateScrollStrategy(strategy) {
- if (strategy === this._scrollStrategy) {
- return;
- }
- this._disposeScrollStrategy();
- this._scrollStrategy = strategy;
- if (this.hasAttached()) {
- strategy.attach(this);
- strategy.enable();
- }
- }
- /**
- * Updates the text direction of the overlay panel.
- * @private
- * @return {?}
- */
- _updateElementDirection() {
- this._host.setAttribute('dir', this.getDirection());
- }
- /**
- * Updates the size of the overlay element based on the overlay config.
- * @private
- * @return {?}
- */
- _updateElementSize() {
- if (!this._pane) {
- return;
- }
- /** @type {?} */
- 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
- * @param {?} enablePointer
- * @return {?}
- */
- _togglePointerEvents(enablePointer) {
- this._pane.style.pointerEvents = enablePointer ? 'auto' : 'none';
- }
- /**
- * Attaches a backdrop for this overlay.
- * @private
- * @return {?}
- */
- _attachBackdrop() {
- /** @type {?} */
- 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.
- (/** @type {?} */ (this._host.parentElement)).insertBefore(this._backdropElement, this._host);
- // 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', this._backdropClickHandler);
- // Add class to fade-in the backdrop after one frame.
- if (typeof requestAnimationFrame !== 'undefined') {
- this._ngZone.runOutsideAngular((/**
- * @return {?}
- */
- () => {
- requestAnimationFrame((/**
- * @return {?}
- */
- () => {
- 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
- * @return {?}
- */
- _updateStackingOrder() {
- if (this._host.nextSibling) {
- (/** @type {?} */ (this._host.parentNode)).appendChild(this._host);
- }
- }
- /**
- * Detaches the backdrop (if any) associated with the overlay.
- * @return {?}
- */
- detachBackdrop() {
- /** @type {?} */
- let backdropToDetach = this._backdropElement;
- if (!backdropToDetach) {
- return;
- }
- /** @type {?} */
- let timeoutId;
- /** @type {?} */
- let finishDetach = (/**
- * @return {?}
- */
- () => {
- // It may not be attached to anything in certain cases (e.g. unit tests).
- if (backdropToDetach) {
- backdropToDetach.removeEventListener('click', this._backdropClickHandler);
- backdropToDetach.removeEventListener('transitionend', finishDetach);
- if (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;
- }
- if (this._config.backdropClass) {
- this._toggleClasses((/** @type {?} */ (backdropToDetach)), this._config.backdropClass, false);
- }
- clearTimeout(timeoutId);
- });
- backdropToDetach.classList.remove('cdk-overlay-backdrop-showing');
- this._ngZone.runOutsideAngular((/**
- * @return {?}
- */
- () => {
- (/** @type {?} */ (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.
- timeoutId = this._ngZone.runOutsideAngular((/**
- * @return {?}
- */
- () => setTimeout(finishDetach, 500)));
- }
- /**
- * Toggles a single CSS class or an array of classes on an element.
- * @private
- * @param {?} element
- * @param {?} cssClasses
- * @param {?} isAdd
- * @return {?}
- */
- _toggleClasses(element, cssClasses, isAdd) {
- /** @type {?} */
- const classList = element.classList;
- coerceArray(cssClasses).forEach((/**
- * @param {?} cssClass
- * @return {?}
- */
- cssClass => {
- // We can't do a spread here, because IE doesn't support setting multiple classes.
- // Also trying to add an empty string to a DOMTokenList will throw.
- if (cssClass) {
- isAdd ? classList.add(cssClass) : classList.remove(cssClass);
- }
- }));
- }
- /**
- * Detaches the overlay content next time the zone stabilizes.
- * @private
- * @return {?}
- */
- _detachContentWhenStable() {
- // Normally we wouldn't have to explicitly run this outside the `NgZone`, however
- // if the consumer is using `zone-patch-rxjs`, the `Subscription.unsubscribe` call will
- // be patched to run inside the zone, which will throw us into an infinite loop.
- this._ngZone.runOutsideAngular((/**
- * @return {?}
- */
- () => {
- // We can't remove the host here immediately, because the overlay pane's content
- // might still be animating. This stream helps us avoid interrupting the animation
- // by waiting for the pane to become empty.
- /** @type {?} */
- const subscription = this._ngZone.onStable
- .asObservable()
- .pipe(takeUntil(merge(this._attachments, this._detachments)))
- .subscribe((/**
- * @return {?}
- */
- () => {
- // Needs a couple of checks for the pane and host, because
- // they may have been removed by the time the zone stabilizes.
- if (!this._pane || !this._host || this._pane.children.length === 0) {
- if (this._pane && this._config.panelClass) {
- this._toggleClasses(this._pane, this._config.panelClass, false);
- }
- if (this._host && this._host.parentElement) {
- this._previousHostParent = this._host.parentElement;
- this._previousHostParent.removeChild(this._host);
- }
- subscription.unsubscribe();
- }
- }));
- }));
- }
- /**
- * Disposes of a scroll strategy.
- * @private
- * @return {?}
- */
- _disposeScrollStrategy() {
- /** @type {?} */
- const scrollStrategy = this._scrollStrategy;
- if (scrollStrategy) {
- scrollStrategy.disable();
- if (scrollStrategy.detach) {
- scrollStrategy.detach();
- }
- }
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- // TODO: refactor clipping detection into a separate thing (part of scrolling module)
- // TODO: doesn't handle both flexible width and height when it has to scroll along both axis.
- /**
- * Class to be added to the overlay bounding box.
- * @type {?}
- */
- const boundingBoxClass = 'cdk-overlay-connected-position-bounding-box';
- /**
- * 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.
- */
- class FlexibleConnectedPositionStrategy {
- /**
- * @param {?} connectedTo
- * @param {?} _viewportRuler
- * @param {?} _document
- * @param {?} _platform
- * @param {?} _overlayContainer
- */
- constructor(connectedTo, _viewportRuler, _document, _platform, _overlayContainer) {
- this._viewportRuler = _viewportRuler;
- this._document = _document;
- this._platform = _platform;
- this._overlayContainer = _overlayContainer;
- /**
- * Last size used for the bounding box. Used to avoid resizing the overlay after open.
- */
- this._lastBoundingBoxSize = { width: 0, height: 0 };
- /**
- * Whether the overlay was pushed in a previous positioning.
- */
- this._isPushed = false;
- /**
- * Whether the overlay can be pushed on-screen on the initial open.
- */
- this._canPush = true;
- /**
- * Whether the overlay can grow via flexible width/height after the initial open.
- */
- this._growAfterOpen = false;
- /**
- * Whether the overlay's width and height can be constrained to fit within the viewport.
- */
- this._hasFlexibleDimensions = true;
- /**
- * Whether the overlay position is locked.
- */
- this._positionLocked = false;
- /**
- * Amount of space that must be maintained between the overlay and the edge of the viewport.
- */
- this._viewportMargin = 0;
- /**
- * The Scrollable containers used to check scrollable view properties on position change.
- */
- this._scrollables = [];
- /**
- * Ordered list of preferred positions, from most to least desirable.
- */
- this._preferredPositions = [];
- /**
- * Subject that emits whenever the position changes.
- */
- this._positionChanges = new Subject();
- /**
- * Subscription to viewport size changes.
- */
- this._resizeSubscription = Subscription.EMPTY;
- /**
- * Default offset for the overlay along the x axis.
- */
- this._offsetX = 0;
- /**
- * Default offset for the overlay along the y axis.
- */
- this._offsetY = 0;
- /**
- * Keeps track of the CSS classes that the position strategy has applied on the overlay panel.
- */
- this._appliedPanelClasses = [];
- /**
- * Observable sequence of position changes.
- */
- this.positionChanges = this._positionChanges.asObservable();
- this.setOrigin(connectedTo);
- }
- /**
- * Ordered list of preferred positions, from most to least desirable.
- * @return {?}
- */
- get positions() {
- return this._preferredPositions;
- }
- /**
- * Attaches this position strategy to an overlay.
- * @param {?} overlayRef
- * @return {?}
- */
- attach(overlayRef) {
- if (this._overlayRef && overlayRef !== this._overlayRef) {
- throw Error('This position strategy is already attached to an overlay');
- }
- this._validatePositions();
- overlayRef.hostElement.classList.add(boundingBoxClass);
- this._overlayRef = overlayRef;
- this._boundingBox = overlayRef.hostElement;
- this._pane = overlayRef.overlayElement;
- this._isDisposed = false;
- this._isInitialRender = true;
- this._lastPosition = null;
- this._resizeSubscription.unsubscribe();
- this._resizeSubscription = this._viewportRuler.change().subscribe((/**
- * @return {?}
- */
- () => {
- // When the window is resized, we want to trigger the next reposition as if it
- // was an initial render, in order for the strategy to pick a new optimal position,
- // otherwise position locking will cause it to stay at the old one.
- this._isInitialRender = true;
- 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
- * @return {?}
- */
- apply() {
- // We shouldn't do anything if the strategy was disposed or we're on the server.
- if (this._isDisposed || !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._clearPanelClasses();
- 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._getOriginRect();
- this._overlayRect = this._pane.getBoundingClientRect();
- /** @type {?} */
- const originRect = this._originRect;
- /** @type {?} */
- const overlayRect = this._overlayRect;
- /** @type {?} */
- const viewportRect = this._viewportRect;
- // Positions where the overlay will fit with flexible dimensions.
- /** @type {?} */
- const flexibleFits = [];
- // Fallback if none of the preferred positions fit within the viewport.
- /** @type {?} */
- let fallback;
- // 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) {
- // Get the exact (x, y) coordinate for the point-of-origin on the origin element.
- /** @type {?} */
- let originPoint = this._getOriginPoint(originRect, pos);
- // 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.
- /** @type {?} */
- let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos);
- // Calculate how well the overlay would fit into the viewport with this point.
- /** @type {?} */
- let overlayFit = this._getOverlayFit(overlayPoint, overlayRect, viewportRect, pos);
- // 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) {
- /** @type {?} */
- let bestFit = null;
- /** @type {?} */
- let bestScore = -1;
- for (const fit of flexibleFits) {
- /** @type {?} */
- const score = fit.boundingBoxRect.width * fit.boundingBoxRect.height * (fit.position.weight || 1);
- if (score > bestScore) {
- bestScore = score;
- bestFit = fit;
- }
- }
- this._isPushed = false;
- this._applyPosition((/** @type {?} */ (bestFit)).position, (/** @type {?} */ (bestFit)).origin);
- 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) {
- // TODO(jelbourn): after pushing, the opening "direction" of the overlay might not make sense.
- this._isPushed = true;
- this._applyPosition((/** @type {?} */ (fallback)).position, (/** @type {?} */ (fallback)).originPoint);
- 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((/** @type {?} */ (fallback)).position, (/** @type {?} */ (fallback)).originPoint);
- }
- /**
- * @return {?}
- */
- detach() {
- this._clearPanelClasses();
- this._lastPosition = null;
- this._previousPushAmount = null;
- this._resizeSubscription.unsubscribe();
- }
- /**
- * Cleanup after the element gets destroyed.
- * @return {?}
- */
- dispose() {
- if (this._isDisposed) {
- return;
- }
- // We can't use `_resetBoundingBoxStyles` here, because it resets
- // some properties to zero, rather than removing them.
- if (this._boundingBox) {
- extendStyles(this._boundingBox.style, (/** @type {?} */ ({
- top: '',
- left: '',
- right: '',
- bottom: '',
- height: '',
- width: '',
- alignItems: '',
- justifyContent: '',
- })));
- }
- if (this._pane) {
- this._resetOverlayElementStyles();
- }
- if (this._overlayRef) {
- this._overlayRef.hostElement.classList.remove(boundingBoxClass);
- }
- this.detach();
- this._positionChanges.complete();
- this._overlayRef = this._boundingBox = (/** @type {?} */ (null));
- 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.
- * @return {?}
- */
- reapplyLastPosition() {
- if (!this._isDisposed && (!this._platform || this._platform.isBrowser)) {
- this._originRect = this._getOriginRect();
- this._overlayRect = this._pane.getBoundingClientRect();
- this._viewportRect = this._getNarrowedViewportRect();
- /** @type {?} */
- const lastPosition = this._lastPosition || this._preferredPositions[0];
- /** @type {?} */
- 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.
- * @template THIS
- * @this {THIS}
- * @param {?} scrollables
- * @return {THIS}
- */
- withScrollableContainers(scrollables) {
- (/** @type {?} */ (this))._scrollables = scrollables;
- return (/** @type {?} */ (this));
- }
- /**
- * Adds new preferred positions.
- * @template THIS
- * @this {THIS}
- * @param {?} positions List of positions options for this overlay.
- * @return {THIS}
- */
- withPositions(positions) {
- (/** @type {?} */ (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((/** @type {?} */ ((/** @type {?} */ (this))._lastPosition))) === -1) {
- (/** @type {?} */ (this))._lastPosition = null;
- }
- (/** @type {?} */ (this))._validatePositions();
- return (/** @type {?} */ (this));
- }
- /**
- * Sets a minimum distance the overlay may be positioned to the edge of the viewport.
- * @template THIS
- * @this {THIS}
- * @param {?} margin Required margin between the overlay and the viewport edge in pixels.
- * @return {THIS}
- */
- withViewportMargin(margin) {
- (/** @type {?} */ (this))._viewportMargin = margin;
- return (/** @type {?} */ (this));
- }
- /**
- * Sets whether the overlay's width and height can be constrained to fit within the viewport.
- * @template THIS
- * @this {THIS}
- * @param {?=} flexibleDimensions
- * @return {THIS}
- */
- withFlexibleDimensions(flexibleDimensions = true) {
- (/** @type {?} */ (this))._hasFlexibleDimensions = flexibleDimensions;
- return (/** @type {?} */ (this));
- }
- /**
- * Sets whether the overlay can grow after the initial open via flexible width/height.
- * @template THIS
- * @this {THIS}
- * @param {?=} growAfterOpen
- * @return {THIS}
- */
- withGrowAfterOpen(growAfterOpen = true) {
- (/** @type {?} */ (this))._growAfterOpen = growAfterOpen;
- return (/** @type {?} */ (this));
- }
- /**
- * Sets whether the overlay can be pushed on-screen if none of the provided positions fit.
- * @template THIS
- * @this {THIS}
- * @param {?=} canPush
- * @return {THIS}
- */
- withPush(canPush = true) {
- (/** @type {?} */ (this))._canPush = canPush;
- return (/** @type {?} */ (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).
- * @template THIS
- * @this {THIS}
- * @param {?=} isLocked Whether the overlay should locked in.
- * @return {THIS}
- */
- withLockedPosition(isLocked = true) {
- (/** @type {?} */ (this))._positionLocked = isLocked;
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the origin, relative to which to position the overlay.
- * Using an element origin is useful for building components that need to be positioned
- * relatively to a trigger (e.g. dropdown menus or tooltips), whereas using a point can be
- * used for cases like contextual menus which open relative to the user's pointer.
- * @template THIS
- * @this {THIS}
- * @param {?} origin Reference to the new origin.
- * @return {THIS}
- */
- setOrigin(origin) {
- (/** @type {?} */ (this))._origin = origin;
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the default offset for the overlay's connection point on the x-axis.
- * @template THIS
- * @this {THIS}
- * @param {?} offset New offset in the X axis.
- * @return {THIS}
- */
- withDefaultOffsetX(offset) {
- (/** @type {?} */ (this))._offsetX = offset;
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the default offset for the overlay's connection point on the y-axis.
- * @template THIS
- * @this {THIS}
- * @param {?} offset New offset in the Y axis.
- * @return {THIS}
- */
- withDefaultOffsetY(offset) {
- (/** @type {?} */ (this))._offsetY = offset;
- return (/** @type {?} */ (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.
- * @template THIS
- * @this {THIS}
- * @param {?} selector CSS selector that will be used to find the target
- * elements onto which to set the transform origin.
- * @return {THIS}
- */
- withTransformOriginOn(selector) {
- (/** @type {?} */ (this))._transformOriginSelector = selector;
- return (/** @type {?} */ (this));
- }
- /**
- * Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
- * @private
- * @param {?} originRect
- * @param {?} pos
- * @return {?}
- */
- _getOriginPoint(originRect, pos) {
- /** @type {?} */
- let x;
- 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);
- }
- else {
- /** @type {?} */
- const startX = this._isRtl() ? originRect.right : originRect.left;
- /** @type {?} */
- const endX = this._isRtl() ? originRect.left : originRect.right;
- x = pos.originX == 'start' ? startX : endX;
- }
- /** @type {?} */
- let y;
- if (pos.originY == 'center') {
- y = originRect.top + (originRect.height / 2);
- }
- 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
- * @param {?} originPoint
- * @param {?} overlayRect
- * @param {?} pos
- * @return {?}
- */
- _getOverlayPoint(originPoint, overlayRect, pos) {
- // Calculate the (overlayStartX, overlayStartY), the start of the
- // potential overlay position relative to the origin point.
- /** @type {?} */
- let overlayStartX;
- if (pos.overlayX == 'center') {
- overlayStartX = -overlayRect.width / 2;
- }
- else if (pos.overlayX === 'start') {
- overlayStartX = this._isRtl() ? -overlayRect.width : 0;
- }
- else {
- overlayStartX = this._isRtl() ? 0 : -overlayRect.width;
- }
- /** @type {?} */
- let overlayStartY;
- if (pos.overlayY == 'center') {
- overlayStartY = -overlayRect.height / 2;
- }
- 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
- * @param {?} point
- * @param {?} overlay
- * @param {?} viewport
- * @param {?} position
- * @return {?}
- */
- _getOverlayFit(point, overlay, viewport, position) {
- let { x, y } = point;
- /** @type {?} */
- let offsetX = this._getOffset(position, 'x');
- /** @type {?} */
- let offsetY = this._getOffset(position, 'y');
- // 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.
- /** @type {?} */
- let leftOverflow = 0 - x;
- /** @type {?} */
- let rightOverflow = (x + overlay.width) - viewport.width;
- /** @type {?} */
- let topOverflow = 0 - y;
- /** @type {?} */
- let bottomOverflow = (y + overlay.height) - viewport.height;
- // Visible parts of the element on each axis.
- /** @type {?} */
- let visibleWidth = this._subtractOverflows(overlay.width, leftOverflow, rightOverflow);
- /** @type {?} */
- let visibleHeight = this._subtractOverflows(overlay.height, topOverflow, bottomOverflow);
- /** @type {?} */
- let visibleArea = visibleWidth * visibleHeight;
- 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.
- * @private
- * @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.
- * @return {?}
- */
- _canFitWithFlexibleDimensions(fit, point, viewport) {
- if (this._hasFlexibleDimensions) {
- /** @type {?} */
- const availableHeight = viewport.bottom - point.y;
- /** @type {?} */
- const availableWidth = viewport.right - point.x;
- /** @type {?} */
- const minHeight = this._overlayRef.getConfig().minHeight;
- /** @type {?} */
- const minWidth = this._overlayRef.getConfig().minWidth;
- /** @type {?} */
- const verticalFit = fit.fitsInViewportVertically ||
- (minHeight != null && minHeight <= availableHeight);
- /** @type {?} */
- const horizontalFit = fit.fitsInViewportHorizontally ||
- (minWidth != null && minWidth <= availableWidth);
- return verticalFit && horizontalFit;
- }
- return false;
- }
- /**
- * 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).
- *
- * @private
- * @param {?} start Starting point from which the overlay is pushed.
- * @param {?} overlay Dimensions of the overlay.
- * @param {?} scrollPosition Current viewport scroll position.
- * @return {?} The point at which to position the overlay after pushing. This is effectively a new
- * originPoint.
- */
- _pushOverlayOnScreen(start, overlay, scrollPosition) {
- // If the position is locked and we've pushed the overlay already, reuse the previous push
- // amount, rather than pushing it again. If we were to continue pushing, the element would
- // remain in the viewport, which goes against the expectations when position locking is enabled.
- if (this._previousPushAmount && this._positionLocked) {
- return {
- x: start.x + this._previousPushAmount.x,
- y: start.y + this._previousPushAmount.y
- };
- }
- /** @type {?} */
- 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.
- /** @type {?} */
- const overflowRight = Math.max(start.x + overlay.width - viewport.right, 0);
- /** @type {?} */
- const overflowBottom = Math.max(start.y + overlay.height - viewport.bottom, 0);
- /** @type {?} */
- const overflowTop = Math.max(viewport.top - scrollPosition.top - start.y, 0);
- /** @type {?} */
- const overflowLeft = Math.max(viewport.left - scrollPosition.left - start.x, 0);
- // Amount by which to push the overlay in each axis such that it remains on-screen.
- /** @type {?} */
- let pushX = 0;
- /** @type {?} */
- let pushY = 0;
- // 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 = start.x < this._viewportMargin ? (viewport.left - scrollPosition.left) - start.x : 0;
- }
- if (overlay.height <= viewport.height) {
- pushY = overflowTop || -overflowBottom;
- }
- else {
- pushY = start.y < this._viewportMargin ? (viewport.top - scrollPosition.top) - start.y : 0;
- }
- this._previousPushAmount = { x: pushX, y: pushY };
- return {
- x: start.x + pushX,
- y: start.y + pushY,
- };
- }
- /**
- * Applies a computed position to the overlay and emits a position change.
- * @private
- * @param {?} position The position preference
- * @param {?} originPoint The point on the origin element where the overlay is connected.
- * @return {?}
- */
- _applyPosition(position, originPoint) {
- this._setTransformOrigin(position);
- this._setOverlayElementStyles(originPoint, position);
- this._setBoundingBoxStyles(originPoint, position);
- if (position.panelClass) {
- this._addPanelClasses(position.panelClass);
- }
- // 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) {
- /** @type {?} */
- const scrollableViewProperties = this._getScrollVisibility();
- /** @type {?} */
- 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
- * @param {?} position
- * @return {?}
- */
- _setTransformOrigin(position) {
- if (!this._transformOriginSelector) {
- return;
- }
- /** @type {?} */
- const elements = (/** @type {?} */ (this._boundingBox)).querySelectorAll(this._transformOriginSelector);
- /** @type {?} */
- let xOrigin;
- /** @type {?} */
- let yOrigin = position.overlayY;
- 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++) {
- 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
- * @param {?} origin
- * @param {?} position
- * @return {?}
- */
- _calculateBoundingBoxRect(origin, position) {
- /** @type {?} */
- const viewport = this._viewportRect;
- /** @type {?} */
- const isRtl = this._isRtl();
- /** @type {?} */
- let height;
- /** @type {?} */
- let top;
- /** @type {?} */
- let bottom;
- if (position.overlayY === 'top') {
- // Overlay is opening "downward" and thus is bound by the bottom viewport edge.
- top = origin.y;
- height = viewport.height - top + this._viewportMargin;
- }
- 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;
- height = viewport.height - bottom + this._viewportMargin;
- }
- else {
- // If neither top nor bottom, it means that the overlay is vertically centered on the
- // origin point. Note that we want the position relative to the viewport, rather than
- // the page, which is why we don't use something like `viewport.bottom - origin.y` and
- // `origin.y - viewport.top`.
- /** @type {?} */
- const smallestDistanceToViewportEdge = Math.min(viewport.bottom - origin.y + viewport.top, origin.y);
- /** @type {?} */
- const previousHeight = this._lastBoundingBoxSize.height;
- height = smallestDistanceToViewportEdge * 2;
- top = origin.y - smallestDistanceToViewportEdge;
- if (height > previousHeight && !this._isInitialRender && !this._growAfterOpen) {
- top = origin.y - (previousHeight / 2);
- }
- }
- // The overlay is opening 'right-ward' (the content flows to the right).
- /** @type {?} */
- const isBoundedByRightViewportEdge = (position.overlayX === 'start' && !isRtl) ||
- (position.overlayX === 'end' && isRtl);
- // The overlay is opening 'left-ward' (the content flows to the left).
- /** @type {?} */
- const isBoundedByLeftViewportEdge = (position.overlayX === 'end' && !isRtl) ||
- (position.overlayX === 'start' && isRtl);
- /** @type {?} */
- let width;
- /** @type {?} */
- let left;
- /** @type {?} */
- let right;
- if (isBoundedByLeftViewportEdge) {
- right = viewport.width - origin.x + this._viewportMargin;
- width = origin.x - this._viewportMargin;
- }
- 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. Note that we want the position relative to the viewport, rather than
- // the page, which is why we don't use something like `viewport.right - origin.x` and
- // `origin.x - viewport.left`.
- /** @type {?} */
- const smallestDistanceToViewportEdge = Math.min(viewport.right - origin.x + viewport.left, origin.x);
- /** @type {?} */
- const previousWidth = this._lastBoundingBoxSize.width;
- width = smallestDistanceToViewportEdge * 2;
- left = origin.x - smallestDistanceToViewportEdge;
- if (width > previousWidth && !this._isInitialRender && !this._growAfterOpen) {
- left = origin.x - (previousWidth / 2);
- }
- }
- return { top: (/** @type {?} */ (top)), left: (/** @type {?} */ (left)), bottom: (/** @type {?} */ (bottom)), right: (/** @type {?} */ (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.
- *
- * @private
- * @param {?} origin The point on the origin element where the overlay is connected.
- * @param {?} position The position preference
- * @return {?}
- */
- _setBoundingBoxStyles(origin, position) {
- /** @type {?} */
- 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);
- }
- /** @type {?} */
- const styles = (/** @type {?} */ ({}));
- if (this._hasExactPosition()) {
- styles.top = styles.left = '0';
- styles.bottom = styles.right = '';
- styles.width = styles.height = '100%';
- }
- else {
- /** @type {?} */
- const maxHeight = this._overlayRef.getConfig().maxHeight;
- /** @type {?} */
- 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((/** @type {?} */ (this._boundingBox)).style, styles);
- }
- /**
- * Resets the styles for the bounding box so that a new positioning can be computed.
- * @private
- * @return {?}
- */
- _resetBoundingBoxStyles() {
- extendStyles((/** @type {?} */ (this._boundingBox)).style, (/** @type {?} */ ({
- top: '0',
- left: '0',
- right: '0',
- bottom: '0',
- height: '',
- width: '',
- alignItems: '',
- justifyContent: '',
- })));
- }
- /**
- * Resets the styles for the overlay pane so that a new positioning can be computed.
- * @private
- * @return {?}
- */
- _resetOverlayElementStyles() {
- extendStyles(this._pane.style, (/** @type {?} */ ({
- top: '',
- left: '',
- bottom: '',
- right: '',
- position: '',
- transform: '',
- })));
- }
- /**
- * Sets positioning styles to the overlay element.
- * @private
- * @param {?} originPoint
- * @param {?} position
- * @return {?}
- */
- _setOverlayElementStyles(originPoint, position) {
- /** @type {?} */
- const styles = (/** @type {?} */ ({}));
- if (this._hasExactPosition()) {
- /** @type {?} */
- const scrollPosition = this._viewportRuler.getViewportScrollPosition();
- extendStyles(styles, this._getExactOverlayY(position, originPoint, scrollPosition));
- extendStyles(styles, this._getExactOverlayX(position, originPoint, scrollPosition));
- }
- 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.
- /** @type {?} */
- let transformString = '';
- /** @type {?} */
- let offsetX = this._getOffset(position, 'x');
- /** @type {?} */
- let offsetY = this._getOffset(position, 'y');
- 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
- * @param {?} position
- * @param {?} originPoint
- * @param {?} scrollPosition
- * @return {?}
- */
- _getExactOverlayY(position, originPoint, scrollPosition) {
- // Reset any existing styles. This is necessary in case the
- // preferred position has changed since the last `apply`.
- /** @type {?} */
- let styles = (/** @type {?} */ ({ top: null, bottom: null }));
- /** @type {?} */
- let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position);
- if (this._isPushed) {
- overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition);
- }
- /** @type {?} */
- let virtualKeyboardOffset = this._overlayContainer.getContainerElement().getBoundingClientRect().top;
- // Normally this would be zero, however when the overlay is attached to an input (e.g. in an
- // autocomplete), mobile browsers will shift everything in order to put the input in the middle
- // of the screen and to make space for the virtual keyboard. We need to account for this offset,
- // otherwise our positioning will be thrown off.
- overlayPoint.y -= virtualKeyboardOffset;
- // 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.
- /** @type {?} */
- const documentHeight = (/** @type {?} */ (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
- * @param {?} position
- * @param {?} originPoint
- * @param {?} scrollPosition
- * @return {?}
- */
- _getExactOverlayX(position, originPoint, scrollPosition) {
- // Reset any existing styles. This is necessary in case the preferred position has
- // changed since the last `apply`.
- /** @type {?} */
- let styles = (/** @type {?} */ ({ left: null, right: null }));
- /** @type {?} */
- let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position);
- if (this._isPushed) {
- overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition);
- }
- // 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.
- /** @type {?} */
- let horizontalStyleProperty;
- 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') {
- /** @type {?} */
- const documentWidth = (/** @type {?} */ (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
- * @return {?}
- */
- _getScrollVisibility() {
- // Note: needs fresh rects since the position could've changed.
- /** @type {?} */
- const originBounds = this._getOriginRect();
- /** @type {?} */
- const overlayBounds = this._pane.getBoundingClientRect();
- // TODO(jelbourn): instead of needing all of the client rects for these scrolling containers
- // every time, we should be able to use the scrollTop of the containers if the size of those
- // containers hasn't changed.
- /** @type {?} */
- const scrollContainerBounds = this._scrollables.map((/**
- * @param {?} scrollable
- * @return {?}
- */
- 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 its length.
- * @private
- * @param {?} length
- * @param {...?} overflows
- * @return {?}
- */
- _subtractOverflows(length, ...overflows) {
- return overflows.reduce((/**
- * @param {?} currentValue
- * @param {?} currentOverflow
- * @return {?}
- */
- (currentValue, currentOverflow) => {
- return currentValue - Math.max(currentOverflow, 0);
- }), length);
- }
- /**
- * Narrows the given viewport rect by the current _viewportMargin.
- * @private
- * @return {?}
- */
- _getNarrowedViewportRect() {
- // 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.
- /** @type {?} */
- const width = (/** @type {?} */ (this._document.documentElement)).clientWidth;
- /** @type {?} */
- const height = (/** @type {?} */ (this._document.documentElement)).clientHeight;
- /** @type {?} */
- 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),
- height: height - (2 * this._viewportMargin),
- };
- }
- /**
- * Whether the we're dealing with an RTL context
- * @private
- * @return {?}
- */
- _isRtl() {
- return this._overlayRef.getDirection() === 'rtl';
- }
- /**
- * Determines whether the overlay uses exact or flexible positioning.
- * @private
- * @return {?}
- */
- _hasExactPosition() {
- return !this._hasFlexibleDimensions || this._isPushed;
- }
- /**
- * Retrieves the offset of a position along the x or y axis.
- * @private
- * @param {?} position
- * @param {?} axis
- * @return {?}
- */
- _getOffset(position, axis) {
- 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
- * @return {?}
- */
- _validatePositions() {
- 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((/**
- * @param {?} pair
- * @return {?}
- */
- pair => {
- validateHorizontalPosition('originX', pair.originX);
- validateVerticalPosition('originY', pair.originY);
- validateHorizontalPosition('overlayX', pair.overlayX);
- validateVerticalPosition('overlayY', pair.overlayY);
- }));
- }
- /**
- * Adds a single CSS class or an array of classes on the overlay panel.
- * @private
- * @param {?} cssClasses
- * @return {?}
- */
- _addPanelClasses(cssClasses) {
- if (this._pane) {
- coerceArray(cssClasses).forEach((/**
- * @param {?} cssClass
- * @return {?}
- */
- cssClass => {
- if (cssClass !== '' && this._appliedPanelClasses.indexOf(cssClass) === -1) {
- this._appliedPanelClasses.push(cssClass);
- this._pane.classList.add(cssClass);
- }
- }));
- }
- }
- /**
- * Clears the classes that the position strategy has applied from the overlay panel.
- * @private
- * @return {?}
- */
- _clearPanelClasses() {
- if (this._pane) {
- this._appliedPanelClasses.forEach((/**
- * @param {?} cssClass
- * @return {?}
- */
- cssClass => {
- this._pane.classList.remove(cssClass);
- }));
- this._appliedPanelClasses = [];
- }
- }
- /**
- * Returns the ClientRect of the current origin.
- * @private
- * @return {?}
- */
- _getOriginRect() {
- /** @type {?} */
- const origin = this._origin;
- if (origin instanceof ElementRef) {
- return origin.nativeElement.getBoundingClientRect();
- }
- if (origin instanceof HTMLElement) {
- return origin.getBoundingClientRect();
- }
- /** @type {?} */
- const width = origin.width || 0;
- /** @type {?} */
- const height = origin.height || 0;
- // If the origin is a point, return a client rect as if it was a 0x0 element at the point.
- return {
- top: origin.y,
- bottom: origin.y + height,
- left: origin.x,
- right: origin.x + width,
- height,
- width
- };
- }
- }
- /**
- * Shallow-extends a stylesheet object with another stylesheet object.
- * @param {?} dest
- * @param {?} source
- * @return {?}
- */
- function extendStyles(dest, source) {
- for (let key in source) {
- if (source.hasOwnProperty(key)) {
- dest[key] = source[key];
- }
- }
- return dest;
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * 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.
- * \@breaking-change 8.0.0
- */
- class ConnectedPositionStrategy {
- /**
- * @param {?} originPos
- * @param {?} overlayPos
- * @param {?} connectedTo
- * @param {?} viewportRuler
- * @param {?} document
- * @param {?} platform
- * @param {?} overlayContainer
- */
- constructor(originPos, overlayPos, connectedTo, viewportRuler, document, platform, overlayContainer) {
- /**
- * Ordered list of preferred positions, from most to least desirable.
- */
- this._preferredPositions = [];
- // 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, overlayContainer)
- .withFlexibleDimensions(false)
- .withPush(false)
- .withViewportMargin(0);
- this.withFallbackPosition(originPos, overlayPos);
- }
- /**
- * Whether the we're dealing with an RTL context
- * @return {?}
- */
- get _isRtl() {
- return this._overlayRef.getDirection() === 'rtl';
- }
- /**
- * Emits an event when the connection point changes.
- * @return {?}
- */
- get onPositionChange() {
- return this._positionStrategy.positionChanges;
- }
- /**
- * Ordered list of preferred positions, from most to least desirable.
- * @return {?}
- */
- get positions() {
- return this._preferredPositions;
- }
- /**
- * Attach this position strategy to an overlay.
- * @param {?} overlayRef
- * @return {?}
- */
- attach(overlayRef) {
- 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.
- * @return {?}
- */
- dispose() {
- this._positionStrategy.dispose();
- }
- /**
- * \@docs-private
- * @return {?}
- */
- detach() {
- this._positionStrategy.detach();
- }
- /**
- * Updates the position of the overlay element, using whichever preferred position relative
- * to the origin fits on-screen.
- * \@docs-private
- * @return {?}
- */
- apply() {
- 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.
- * @return {?}
- */
- recalculateLastPosition() {
- 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.
- * @param {?} scrollables
- * @return {?}
- */
- withScrollableContainers(scrollables) {
- this._positionStrategy.withScrollableContainers(scrollables);
- }
- /**
- * Adds a new preferred fallback position.
- * @template THIS
- * @this {THIS}
- * @param {?} originPos
- * @param {?} overlayPos
- * @param {?=} offsetX
- * @param {?=} offsetY
- * @return {THIS}
- */
- withFallbackPosition(originPos, overlayPos, offsetX, offsetY) {
- /** @type {?} */
- const position = new ConnectionPositionPair(originPos, overlayPos, offsetX, offsetY);
- (/** @type {?} */ (this))._preferredPositions.push(position);
- (/** @type {?} */ (this))._positionStrategy.withPositions((/** @type {?} */ (this))._preferredPositions);
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the layout direction so the overlay's position can be adjusted to match.
- * @template THIS
- * @this {THIS}
- * @param {?} dir New layout direction.
- * @return {THIS}
- */
- withDirection(dir) {
- // 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 ((/** @type {?} */ (this))._overlayRef) {
- (/** @type {?} */ (this))._overlayRef.setDirection(dir);
- }
- else {
- (/** @type {?} */ (this))._direction = dir;
- }
- return (/** @type {?} */ (this));
- }
- /**
- * Sets an offset for the overlay's connection point on the x-axis
- * @template THIS
- * @this {THIS}
- * @param {?} offset New offset in the X axis.
- * @return {THIS}
- */
- withOffsetX(offset) {
- (/** @type {?} */ (this))._positionStrategy.withDefaultOffsetX(offset);
- return (/** @type {?} */ (this));
- }
- /**
- * Sets an offset for the overlay's connection point on the y-axis
- * @template THIS
- * @this {THIS}
- * @param {?} offset New offset in the Y axis.
- * @return {THIS}
- */
- withOffsetY(offset) {
- (/** @type {?} */ (this))._positionStrategy.withDefaultOffsetY(offset);
- return (/** @type {?} */ (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).
- * @template THIS
- * @this {THIS}
- * @param {?} isLocked Whether the overlay should locked in.
- * @return {THIS}
- */
- withLockedPosition(isLocked) {
- (/** @type {?} */ (this))._positionStrategy.withLockedPosition(isLocked);
- return (/** @type {?} */ (this));
- }
- /**
- * Overwrites the current set of positions with an array of new ones.
- * @template THIS
- * @this {THIS}
- * @param {?} positions Position pairs to be set on the strategy.
- * @return {THIS}
- */
- withPositions(positions) {
- (/** @type {?} */ (this))._preferredPositions = positions.slice();
- (/** @type {?} */ (this))._positionStrategy.withPositions((/** @type {?} */ (this))._preferredPositions);
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the origin element, relative to which to position the overlay.
- * @template THIS
- * @this {THIS}
- * @param {?} origin Reference to the new origin element.
- * @return {THIS}
- */
- setOrigin(origin) {
- (/** @type {?} */ (this))._positionStrategy.setOrigin(origin);
- return (/** @type {?} */ (this));
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Class to be added to the overlay pane wrapper.
- * @type {?}
- */
- const wrapperClass = 'cdk-global-overlay-wrapper';
- /**
- * 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.
- */
- class GlobalPositionStrategy {
- constructor() {
- this._cssPosition = 'static';
- this._topOffset = '';
- this._bottomOffset = '';
- this._leftOffset = '';
- this._rightOffset = '';
- this._alignItems = '';
- this._justifyContent = '';
- this._width = '';
- this._height = '';
- }
- /**
- * @param {?} overlayRef
- * @return {?}
- */
- attach(overlayRef) {
- /** @type {?} */
- 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(wrapperClass);
- this._isDisposed = false;
- }
- /**
- * Sets the top position of the overlay. Clears any previously set vertical position.
- * @template THIS
- * @this {THIS}
- * @param {?=} value New top offset.
- * @return {THIS}
- */
- top(value = '') {
- (/** @type {?} */ (this))._bottomOffset = '';
- (/** @type {?} */ (this))._topOffset = value;
- (/** @type {?} */ (this))._alignItems = 'flex-start';
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the left position of the overlay. Clears any previously set horizontal position.
- * @template THIS
- * @this {THIS}
- * @param {?=} value New left offset.
- * @return {THIS}
- */
- left(value = '') {
- (/** @type {?} */ (this))._rightOffset = '';
- (/** @type {?} */ (this))._leftOffset = value;
- (/** @type {?} */ (this))._justifyContent = 'flex-start';
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the bottom position of the overlay. Clears any previously set vertical position.
- * @template THIS
- * @this {THIS}
- * @param {?=} value New bottom offset.
- * @return {THIS}
- */
- bottom(value = '') {
- (/** @type {?} */ (this))._topOffset = '';
- (/** @type {?} */ (this))._bottomOffset = value;
- (/** @type {?} */ (this))._alignItems = 'flex-end';
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the right position of the overlay. Clears any previously set horizontal position.
- * @template THIS
- * @this {THIS}
- * @param {?=} value New right offset.
- * @return {THIS}
- */
- right(value = '') {
- (/** @type {?} */ (this))._leftOffset = '';
- (/** @type {?} */ (this))._rightOffset = value;
- (/** @type {?} */ (this))._justifyContent = 'flex-end';
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the overlay width and clears any previously set width.
- * @deprecated Pass the `width` through the `OverlayConfig`.
- * \@breaking-change 8.0.0
- * @template THIS
- * @this {THIS}
- * @param {?=} value New width for the overlay
- * @return {THIS}
- */
- width(value = '') {
- if ((/** @type {?} */ (this))._overlayRef) {
- (/** @type {?} */ (this))._overlayRef.updateSize({ width: value });
- }
- else {
- (/** @type {?} */ (this))._width = value;
- }
- return (/** @type {?} */ (this));
- }
- /**
- * Sets the overlay height and clears any previously set height.
- * @deprecated Pass the `height` through the `OverlayConfig`.
- * \@breaking-change 8.0.0
- * @template THIS
- * @this {THIS}
- * @param {?=} value New height for the overlay
- * @return {THIS}
- */
- height(value = '') {
- if ((/** @type {?} */ (this))._overlayRef) {
- (/** @type {?} */ (this))._overlayRef.updateSize({ height: value });
- }
- else {
- (/** @type {?} */ (this))._height = value;
- }
- return (/** @type {?} */ (this));
- }
- /**
- * Centers the overlay horizontally with an optional offset.
- * Clears any previously set horizontal position.
- *
- * @template THIS
- * @this {THIS}
- * @param {?=} offset Overlay offset from the horizontal center.
- * @return {THIS}
- */
- centerHorizontally(offset = '') {
- (/** @type {?} */ (this)).left(offset);
- (/** @type {?} */ (this))._justifyContent = 'center';
- return (/** @type {?} */ (this));
- }
- /**
- * Centers the overlay vertically with an optional offset.
- * Clears any previously set vertical position.
- *
- * @template THIS
- * @this {THIS}
- * @param {?=} offset Overlay offset from the vertical center.
- * @return {THIS}
- */
- centerVertically(offset = '') {
- (/** @type {?} */ (this)).top(offset);
- (/** @type {?} */ (this))._alignItems = 'center';
- return (/** @type {?} */ (this));
- }
- /**
- * Apply the position to the element.
- * \@docs-private
- * @return {?}
- */
- apply() {
- // 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 || !this._overlayRef.hasAttached()) {
- return;
- }
- /** @type {?} */
- const styles = this._overlayRef.overlayElement.style;
- /** @type {?} */
- const parentStyles = this._overlayRef.hostElement.style;
- /** @type {?} */
- 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._justifyContent === 'center') {
- parentStyles.justifyContent = 'center';
- }
- 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.
- // TODO: reconsider this if we add `start` and `end` methods.
- 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;
- }
- /**
- * Cleans up the DOM changes from the position strategy.
- * \@docs-private
- * @return {?}
- */
- dispose() {
- if (this._isDisposed || !this._overlayRef) {
- return;
- }
- /** @type {?} */
- const styles = this._overlayRef.overlayElement.style;
- /** @type {?} */
- const parent = this._overlayRef.hostElement;
- /** @type {?} */
- const parentStyles = parent.style;
- parent.classList.remove(wrapperClass);
- parentStyles.justifyContent = parentStyles.alignItems = styles.marginTop =
- styles.marginBottom = styles.marginLeft = styles.marginRight = styles.position = '';
- this._overlayRef = (/** @type {?} */ (null));
- this._isDisposed = true;
- }
- }
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Builder for overlay position strategy.
- */
- class OverlayPositionBuilder {
- /**
- * @param {?} _viewportRuler
- * @param {?} _document
- * @param {?} _platform
- * @param {?} _overlayContainer
- */
- constructor(_viewportRuler, _document, _platform, _overlayContainer) {
- this._viewportRuler = _viewportRuler;
- this._document = _document;
- this._platform = _platform;
- this._overlayContainer = _overlayContainer;
- }
- /**
- * Creates a global position strategy.
- * @return {?}
- */
- global() {
- return new GlobalPositionStrategy();
- }
- /**
- * Creates a relative position strategy.
- * @deprecated Use `flexibleConnectedTo` instead.
- * \@breaking-change 8.0.0
- * @param {?} elementRef
- * @param {?} originPos
- * @param {?} overlayPos
- * @return {?}
- */
- connectedTo(elementRef, originPos, overlayPos) {
- return new ConnectedPositionStrategy(originPos, overlayPos, elementRef, this._viewportRuler, this._document, this._platform, this._overlayContainer);
- }
- /**
- * Creates a flexible position strategy.
- * @param {?} origin Origin relative to which to position the overlay.
- * @return {?}
- */
- flexibleConnectedTo(origin) {
- return new FlexibleConnectedPositionStrategy(origin, this._viewportRuler, this._document, this._platform, this._overlayContainer);
- }
- }
- OverlayPositionBuilder.decorators = [
- { type: Injectable, args: [{ providedIn: 'root' },] },
- ];
- /** @nocollapse */
- OverlayPositionBuilder.ctorParameters = () => [
- { type: ViewportRuler },
- { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
- { type: Platform },
- { type: OverlayContainer }
- ];
- /** @nocollapse */ OverlayPositionBuilder.ngInjectableDef = ɵɵdefineInjectable({ factory: function OverlayPositionBuilder_Factory() { return new OverlayPositionBuilder(ɵɵinject(ViewportRuler), ɵɵinject(DOCUMENT), ɵɵinject(Platform), ɵɵinject(OverlayContainer)); }, token: OverlayPositionBuilder, providedIn: "root" });
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Next overlay unique ID.
- * @type {?}
- */
- 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.
- */
- class Overlay {
- /**
- * @param {?} scrollStrategies
- * @param {?} _overlayContainer
- * @param {?} _componentFactoryResolver
- * @param {?} _positionBuilder
- * @param {?} _keyboardDispatcher
- * @param {?} _injector
- * @param {?} _ngZone
- * @param {?} _document
- * @param {?} _directionality
- * @param {?=} _location
- */
- constructor(scrollStrategies, _overlayContainer, _componentFactoryResolver, _positionBuilder, _keyboardDispatcher, _injector, _ngZone, _document, _directionality, _location) {
- this.scrollStrategies = scrollStrategies;
- this._overlayContainer = _overlayContainer;
- this._componentFactoryResolver = _componentFactoryResolver;
- this._positionBuilder = _positionBuilder;
- this._keyboardDispatcher = _keyboardDispatcher;
- this._injector = _injector;
- this._ngZone = _ngZone;
- this._document = _document;
- this._directionality = _directionality;
- this._location = _location;
- }
- /**
- * Creates an overlay.
- * @param {?=} config Configuration applied to the overlay.
- * @return {?} Reference to the created overlay.
- */
- create(config) {
- /** @type {?} */
- const host = this._createHostElement();
- /** @type {?} */
- const pane = this._createPaneElement(host);
- /** @type {?} */
- const portalOutlet = this._createPortalOutlet(pane);
- /** @type {?} */
- 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, this._location);
- }
- /**
- * Gets a position builder that can be used, via fluent API,
- * to construct and configure a position strategy.
- * @return {?} An overlay position builder.
- */
- position() {
- return this._positionBuilder;
- }
- /**
- * Creates the DOM element for an overlay and appends it to the overlay container.
- * @private
- * @param {?} host
- * @return {?} Newly-created pane element
- */
- _createPaneElement(host) {
- /** @type {?} */
- 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.
- * @private
- * @return {?} Newly-create host element.
- */
- _createHostElement() {
- /** @type {?} */
- const host = this._document.createElement('div');
- this._overlayContainer.getContainerElement().appendChild(host);
- return host;
- }
- /**
- * Create a DomPortalOutlet into which the overlay content can be loaded.
- * @private
- * @param {?} pane The DOM element to turn into a portal outlet.
- * @return {?} A portal outlet for the given DOM element.
- */
- _createPortalOutlet(pane) {
- // 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);
- }
- }
- Overlay.decorators = [
- { type: Injectable },
- ];
- /** @nocollapse */
- Overlay.ctorParameters = () => [
- { type: ScrollStrategyOptions },
- { type: OverlayContainer },
- { type: ComponentFactoryResolver },
- { type: OverlayPositionBuilder },
- { type: OverlayKeyboardDispatcher },
- { type: Injector },
- { type: NgZone },
- { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
- { type: Directionality },
- { type: Location, decorators: [{ type: Optional }] }
- ];
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * Default set of positions for the overlay. Follows the behavior of a dropdown.
- * @type {?}
- */
- const defaultPositionList = [
- {
- 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.
- * @type {?}
- */
- const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY = new InjectionToken('cdk-connected-overlay-scroll-strategy');
- /**
- * Directive applied to an element to make it usable as an origin for an Overlay using a
- * ConnectedPositionStrategy.
- */
- class CdkOverlayOrigin {
- /**
- * @param {?} elementRef
- */
- constructor(elementRef) {
- this.elementRef = elementRef;
- }
- }
- CdkOverlayOrigin.decorators = [
- { type: Directive, args: [{
- selector: '[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]',
- exportAs: 'cdkOverlayOrigin',
- },] },
- ];
- /** @nocollapse */
- CdkOverlayOrigin.ctorParameters = () => [
- { type: ElementRef }
- ];
- /**
- * Directive to facilitate declarative creation of an
- * Overlay using a FlexibleConnectedPositionStrategy.
- */
- class CdkConnectedOverlay {
- // TODO(jelbourn): inputs for size, scroll behavior, animation, etc.
- /**
- * @param {?} _overlay
- * @param {?} templateRef
- * @param {?} viewContainerRef
- * @param {?} scrollStrategyFactory
- * @param {?} _dir
- */
- constructor(_overlay, templateRef, viewContainerRef, scrollStrategyFactory, _dir) {
- this._overlay = _overlay;
- this._dir = _dir;
- this._hasBackdrop = false;
- this._lockPosition = false;
- this._growAfterOpen = false;
- this._flexibleDimensions = false;
- this._push = false;
- this._backdropSubscription = Subscription.EMPTY;
- /**
- * Margin between the overlay and the viewport edges.
- */
- this.viewportMargin = 0;
- /**
- * Whether the overlay is open.
- */
- this.open = false;
- /**
- * Event emitted when the backdrop is clicked.
- */
- this.backdropClick = new EventEmitter();
- /**
- * Event emitted when the position has changed.
- */
- this.positionChange = new EventEmitter();
- /**
- * Event emitted when the overlay has been attached.
- */
- this.attach = new EventEmitter();
- /**
- * Event emitted when the overlay has been detached.
- */
- this.detach = new EventEmitter();
- /**
- * Emits when there are keyboard events that are targeted at the overlay.
- */
- this.overlayKeydown = new EventEmitter();
- this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
- this._scrollStrategyFactory = scrollStrategyFactory;
- this.scrollStrategy = this._scrollStrategyFactory();
- }
- /**
- * The offset in pixels for the overlay connection point on the x-axis
- * @return {?}
- */
- get offsetX() { return this._offsetX; }
- /**
- * @param {?} offsetX
- * @return {?}
- */
- set offsetX(offsetX) {
- this._offsetX = offsetX;
- if (this._position) {
- this._updatePositionStrategy(this._position);
- }
- }
- /**
- * The offset in pixels for the overlay connection point on the y-axis
- * @return {?}
- */
- get offsetY() { return this._offsetY; }
- /**
- * @param {?} offsetY
- * @return {?}
- */
- set offsetY(offsetY) {
- this._offsetY = offsetY;
- if (this._position) {
- this._updatePositionStrategy(this._position);
- }
- }
- /**
- * Whether or not the overlay should attach a backdrop.
- * @return {?}
- */
- get hasBackdrop() { return this._hasBackdrop; }
- /**
- * @param {?} value
- * @return {?}
- */
- set hasBackdrop(value) { this._hasBackdrop = coerceBooleanProperty(value); }
- /**
- * Whether or not the overlay should be locked when scrolling.
- * @return {?}
- */
- get lockPosition() { return this._lockPosition; }
- /**
- * @param {?} value
- * @return {?}
- */
- set lockPosition(value) { this._lockPosition = coerceBooleanProperty(value); }
- /**
- * Whether the overlay's width and height can be constrained to fit within the viewport.
- * @return {?}
- */
- get flexibleDimensions() { return this._flexibleDimensions; }
- /**
- * @param {?} value
- * @return {?}
- */
- set flexibleDimensions(value) {
- this._flexibleDimensions = coerceBooleanProperty(value);
- }
- /**
- * Whether the overlay can grow after the initial open when flexible positioning is turned on.
- * @return {?}
- */
- get growAfterOpen() { return this._growAfterOpen; }
- /**
- * @param {?} value
- * @return {?}
- */
- set growAfterOpen(value) { this._growAfterOpen = coerceBooleanProperty(value); }
- /**
- * Whether the overlay can be pushed on-screen if none of the provided positions fit.
- * @return {?}
- */
- get push() { return this._push; }
- /**
- * @param {?} value
- * @return {?}
- */
- set push(value) { this._push = coerceBooleanProperty(value); }
- /**
- * The associated overlay reference.
- * @return {?}
- */
- get overlayRef() {
- return this._overlayRef;
- }
- /**
- * The element's layout direction.
- * @return {?}
- */
- get dir() {
- return this._dir ? this._dir.value : 'ltr';
- }
- /**
- * @return {?}
- */
- ngOnDestroy() {
- if (this._overlayRef) {
- this._overlayRef.dispose();
- }
- this._backdropSubscription.unsubscribe();
- }
- /**
- * @param {?} changes
- * @return {?}
- */
- ngOnChanges(changes) {
- if (this._position) {
- this._updatePositionStrategy(this._position);
- this._overlayRef.updateSize({
- width: this.width,
- minWidth: this.minWidth,
- height: this.height,
- minHeight: this.minHeight,
- });
- if (changes['origin'] && this.open) {
- this._position.apply();
- }
- }
- if (changes['open']) {
- this.open ? this._attachOverlay() : this._detachOverlay();
- }
- }
- /**
- * Creates an overlay
- * @private
- * @return {?}
- */
- _createOverlay() {
- if (!this.positions || !this.positions.length) {
- this.positions = defaultPositionList;
- }
- this._overlayRef = this._overlay.create(this._buildConfig());
- this._overlayRef.keydownEvents().subscribe((/**
- * @param {?} event
- * @return {?}
- */
- (event) => {
- this.overlayKeydown.next(event);
- if (event.keyCode === ESCAPE && !hasModifierKey(event)) {
- event.preventDefault();
- this._detachOverlay();
- }
- }));
- }
- /**
- * Builds the overlay config based on the directive's inputs
- * @private
- * @return {?}
- */
- _buildConfig() {
- /** @type {?} */
- const positionStrategy = this._position = this._createPositionStrategy();
- /** @type {?} */
- 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;
- }
- if (this.panelClass) {
- overlayConfig.panelClass = this.panelClass;
- }
- return overlayConfig;
- }
- /**
- * Updates the state of a position strategy, based on the values of the directive inputs.
- * @private
- * @param {?} positionStrategy
- * @return {?}
- */
- _updatePositionStrategy(positionStrategy) {
- /** @type {?} */
- const positions = this.positions.map((/**
- * @param {?} currentPosition
- * @return {?}
- */
- currentPosition => ({
- originX: currentPosition.originX,
- originY: currentPosition.originY,
- overlayX: currentPosition.overlayX,
- overlayY: currentPosition.overlayY,
- offsetX: currentPosition.offsetX || this.offsetX,
- offsetY: currentPosition.offsetY || this.offsetY,
- panelClass: currentPosition.panelClass || undefined,
- })));
- return positionStrategy
- .setOrigin(this.origin.elementRef)
- .withPositions(positions)
- .withFlexibleDimensions(this.flexibleDimensions)
- .withPush(this.push)
- .withGrowAfterOpen(this.growAfterOpen)
- .withViewportMargin(this.viewportMargin)
- .withLockedPosition(this.lockPosition);
- }
- /**
- * Returns the position strategy of the overlay to be set on the overlay config
- * @private
- * @return {?}
- */
- _createPositionStrategy() {
- /** @type {?} */
- const strategy = this._overlay.position().flexibleConnectedTo(this.origin.elementRef);
- this._updatePositionStrategy(strategy);
- strategy.positionChanges.subscribe((/**
- * @param {?} p
- * @return {?}
- */
- p => this.positionChange.emit(p)));
- return strategy;
- }
- /**
- * Attaches the overlay and subscribes to backdrop clicks if backdrop exists
- * @private
- * @return {?}
- */
- _attachOverlay() {
- if (!this._overlayRef) {
- this._createOverlay();
- }
- else {
- // Update the overlay size, in case the directive's inputs have changed
- this._overlayRef.getConfig().hasBackdrop = this.hasBackdrop;
- }
- if (!this._overlayRef.hasAttached()) {
- this._overlayRef.attach(this._templatePortal);
- this.attach.emit();
- }
- if (this.hasBackdrop) {
- this._backdropSubscription = this._overlayRef.backdropClick().subscribe((/**
- * @param {?} event
- * @return {?}
- */
- event => {
- this.backdropClick.emit(event);
- }));
- }
- else {
- this._backdropSubscription.unsubscribe();
- }
- }
- /**
- * Detaches the overlay and unsubscribes to backdrop clicks if backdrop exists
- * @private
- * @return {?}
- */
- _detachOverlay() {
- if (this._overlayRef) {
- this._overlayRef.detach();
- this.detach.emit();
- }
- this._backdropSubscription.unsubscribe();
- }
- }
- CdkConnectedOverlay.decorators = [
- { type: Directive, args: [{
- selector: '[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]',
- exportAs: 'cdkConnectedOverlay'
- },] },
- ];
- /** @nocollapse */
- CdkConnectedOverlay.ctorParameters = () => [
- { type: Overlay },
- { type: TemplateRef },
- { type: ViewContainerRef },
- { type: undefined, decorators: [{ type: Inject, args: [CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY,] }] },
- { type: Directionality, decorators: [{ type: Optional }] }
- ];
- CdkConnectedOverlay.propDecorators = {
- origin: [{ type: Input, args: ['cdkConnectedOverlayOrigin',] }],
- positions: [{ type: Input, args: ['cdkConnectedOverlayPositions',] }],
- offsetX: [{ type: Input, args: ['cdkConnectedOverlayOffsetX',] }],
- offsetY: [{ type: Input, args: ['cdkConnectedOverlayOffsetY',] }],
- width: [{ type: Input, args: ['cdkConnectedOverlayWidth',] }],
- height: [{ type: Input, args: ['cdkConnectedOverlayHeight',] }],
- minWidth: [{ type: Input, args: ['cdkConnectedOverlayMinWidth',] }],
- minHeight: [{ type: Input, args: ['cdkConnectedOverlayMinHeight',] }],
- backdropClass: [{ type: Input, args: ['cdkConnectedOverlayBackdropClass',] }],
- panelClass: [{ type: Input, args: ['cdkConnectedOverlayPanelClass',] }],
- viewportMargin: [{ type: Input, args: ['cdkConnectedOverlayViewportMargin',] }],
- scrollStrategy: [{ type: Input, args: ['cdkConnectedOverlayScrollStrategy',] }],
- open: [{ type: Input, args: ['cdkConnectedOverlayOpen',] }],
- hasBackdrop: [{ type: Input, args: ['cdkConnectedOverlayHasBackdrop',] }],
- lockPosition: [{ type: Input, args: ['cdkConnectedOverlayLockPosition',] }],
- flexibleDimensions: [{ type: Input, args: ['cdkConnectedOverlayFlexibleDimensions',] }],
- growAfterOpen: [{ type: Input, args: ['cdkConnectedOverlayGrowAfterOpen',] }],
- push: [{ type: Input, args: ['cdkConnectedOverlayPush',] }],
- backdropClick: [{ type: Output }],
- positionChange: [{ type: Output }],
- attach: [{ type: Output }],
- detach: [{ type: Output }],
- overlayKeydown: [{ type: Output }]
- };
- /**
- * \@docs-private
- * @param {?} overlay
- * @return {?}
- */
- function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
- return (/**
- * @return {?}
- */
- () => overlay.scrollStrategies.reposition());
- }
- /**
- * \@docs-private
- * @type {?}
- */
- const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = {
- provide: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY,
- deps: [Overlay],
- useFactory: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY,
- };
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- class OverlayModule {
- }
- OverlayModule.decorators = [
- { type: NgModule, args: [{
- imports: [BidiModule, PortalModule, ScrollingModule],
- exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollingModule],
- declarations: [CdkConnectedOverlay, CdkOverlayOrigin],
- providers: [
- Overlay,
- CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER,
- ],
- },] },
- ];
- /**
- * @deprecated Use `OverlayModule` instead.
- * \@breaking-change 8.0.0
- * \@docs-private
- * @type {?}
- */
- const OVERLAY_PROVIDERS = [
- Overlay,
- OverlayPositionBuilder,
- OVERLAY_KEYBOARD_DISPATCHER_PROVIDER,
- VIEWPORT_RULER_PROVIDER,
- OVERLAY_CONTAINER_PROVIDER,
- CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER,
- ];
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * 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.
- */
- class FullscreenOverlayContainer extends OverlayContainer {
- /**
- * @param {?} _document
- */
- constructor(_document) {
- super(_document);
- }
- /**
- * @return {?}
- */
- ngOnDestroy() {
- super.ngOnDestroy();
- if (this._fullScreenEventName && this._fullScreenListener) {
- this._document.removeEventListener(this._fullScreenEventName, this._fullScreenListener);
- }
- }
- /**
- * @protected
- * @return {?}
- */
- _createContainer() {
- super._createContainer();
- this._adjustParentForFullscreenChange();
- this._addFullscreenChangeListener((/**
- * @return {?}
- */
- () => this._adjustParentForFullscreenChange()));
- }
- /**
- * @private
- * @return {?}
- */
- _adjustParentForFullscreenChange() {
- if (!this._containerElement) {
- return;
- }
- /** @type {?} */
- const fullscreenElement = this.getFullscreenElement();
- /** @type {?} */
- const parent = fullscreenElement || this._document.body;
- parent.appendChild(this._containerElement);
- }
- /**
- * @private
- * @param {?} fn
- * @return {?}
- */
- _addFullscreenChangeListener(fn) {
- /** @type {?} */
- const eventName = this._getEventName();
- if (eventName) {
- if (this._fullScreenListener) {
- this._document.removeEventListener(eventName, this._fullScreenListener);
- }
- this._document.addEventListener(eventName, fn);
- this._fullScreenListener = fn;
- }
- }
- /**
- * @private
- * @return {?}
- */
- _getEventName() {
- if (!this._fullScreenEventName) {
- /** @type {?} */
- const _document = (/** @type {?} */ (this._document));
- if (_document.fullscreenEnabled) {
- this._fullScreenEventName = 'fullscreenchange';
- }
- else if (_document.webkitFullscreenEnabled) {
- this._fullScreenEventName = 'webkitfullscreenchange';
- }
- else if (_document.mozFullScreenEnabled) {
- this._fullScreenEventName = 'mozfullscreenchange';
- }
- else if (_document.msFullscreenEnabled) {
- this._fullScreenEventName = 'MSFullscreenChange';
- }
- }
- return this._fullScreenEventName;
- }
- /**
- * 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.
- * @return {?}
- */
- getFullscreenElement() {
- /** @type {?} */
- const _document = (/** @type {?} */ (this._document));
- return _document.fullscreenElement ||
- _document.webkitFullscreenElement ||
- _document.mozFullScreenElement ||
- _document.msFullscreenElement ||
- null;
- }
- }
- FullscreenOverlayContainer.decorators = [
- { type: Injectable, args: [{ providedIn: 'root' },] },
- ];
- /** @nocollapse */
- FullscreenOverlayContainer.ctorParameters = () => [
- { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
- ];
- /** @nocollapse */ FullscreenOverlayContainer.ngInjectableDef = ɵɵdefineInjectable({ factory: function FullscreenOverlayContainer_Factory() { return new FullscreenOverlayContainer(ɵɵinject(DOCUMENT)); }, token: FullscreenOverlayContainer, providedIn: "root" });
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- /**
- * @fileoverview added by tsickle
- * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
- */
- export { Overlay, OverlayContainer, CdkOverlayOrigin, CdkConnectedOverlay, FullscreenOverlayContainer, OverlayRef, OverlayKeyboardDispatcher, OverlayPositionBuilder, GlobalPositionStrategy, ConnectedPositionStrategy, FlexibleConnectedPositionStrategy, OverlayConfig, validateVerticalPosition, validateHorizontalPosition, ConnectionPositionPair, ScrollingVisibility, ConnectedOverlayPositionChange, ScrollStrategyOptions, RepositionScrollStrategy, CloseScrollStrategy, NoopScrollStrategy, BlockScrollStrategy, OverlayModule, OVERLAY_PROVIDERS, OVERLAY_KEYBOARD_DISPATCHER_PROVIDER as ɵg, OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY as ɵf, OVERLAY_CONTAINER_PROVIDER as ɵb, OVERLAY_CONTAINER_PROVIDER_FACTORY as ɵa, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY as ɵc, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER as ɵe, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY as ɵd };
- //# sourceMappingURL=overlay.js.map
|