123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664 |
- import * as i0 from '@angular/core';
- import { Injectable, Inject, Optional, InjectionToken, inject, NgZone, ApplicationRef, Injector, createComponent, TemplateRef, Directive, ContentChild, EventEmitter, ViewContainerRef, EnvironmentInjector, Attribute, SkipSelf, Input, Output, reflectComponentType, HostListener, ElementRef, ViewChild } from '@angular/core';
- import * as i3 from '@angular/router';
- import { NavigationStart, PRIMARY_OUTLET, ChildrenOutletContexts, ActivatedRoute, Router } from '@angular/router';
- import * as i1 from '@angular/common';
- import { DOCUMENT } from '@angular/common';
- import { isPlatform, getPlatforms, LIFECYCLE_WILL_ENTER, LIFECYCLE_DID_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_UNLOAD, componentOnReady } from '@ionic/core/components';
- import { Subject, fromEvent, BehaviorSubject, combineLatest, of } from 'rxjs';
- import { __decorate } from 'tslib';
- import { filter, switchMap, distinctUntilChanged } from 'rxjs/operators';
- import { NgControl } from '@angular/forms';
- class MenuController {
- menuController;
- constructor(menuController) {
- this.menuController = menuController;
- }
- /**
- * Programmatically open the Menu.
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return returns a promise when the menu is fully opened
- */
- open(menuId) {
- return this.menuController.open(menuId);
- }
- /**
- * Programmatically close the Menu. If no `menuId` is given as the first
- * argument then it'll close any menu which is open. If a `menuId`
- * is given then it'll close that exact menu.
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return returns a promise when the menu is fully closed
- */
- close(menuId) {
- return this.menuController.close(menuId);
- }
- /**
- * Toggle the menu. If it's closed, it will open, and if opened, it
- * will close.
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return returns a promise when the menu has been toggled
- */
- toggle(menuId) {
- return this.menuController.toggle(menuId);
- }
- /**
- * Used to enable or disable a menu. For example, there could be multiple
- * left menus, but only one of them should be able to be opened at the same
- * time. If there are multiple menus on the same side, then enabling one menu
- * will also automatically disable all the others that are on the same side.
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return Returns the instance of the menu, which is useful for chaining.
- */
- enable(shouldEnable, menuId) {
- return this.menuController.enable(shouldEnable, menuId);
- }
- /**
- * Used to enable or disable the ability to swipe open the menu.
- * @param shouldEnable True if it should be swipe-able, false if not.
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return Returns the instance of the menu, which is useful for chaining.
- */
- swipeGesture(shouldEnable, menuId) {
- return this.menuController.swipeGesture(shouldEnable, menuId);
- }
- /**
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return Returns true if the specified menu is currently open, otherwise false.
- * If the menuId is not specified, it returns true if ANY menu is currenly open.
- */
- isOpen(menuId) {
- return this.menuController.isOpen(menuId);
- }
- /**
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return Returns true if the menu is currently enabled, otherwise false.
- */
- isEnabled(menuId) {
- return this.menuController.isEnabled(menuId);
- }
- /**
- * Used to get a menu instance. If a `menuId` is not provided then it'll
- * return the first menu found. If a `menuId` is `left` or `right`, then
- * it'll return the enabled menu on that side. Otherwise, if a `menuId` is
- * provided, then it'll try to find the menu using the menu's `id`
- * property. If a menu is not found then it'll return `null`.
- * @param [menuId] Optionally get the menu by its id, or side.
- * @return Returns the instance of the menu if found, otherwise `null`.
- */
- get(menuId) {
- return this.menuController.get(menuId);
- }
- /**
- * @return Returns the instance of the menu already opened, otherwise `null`.
- */
- getOpen() {
- return this.menuController.getOpen();
- }
- /**
- * @return Returns an array of all menu instances.
- */
- getMenus() {
- return this.menuController.getMenus();
- }
- registerAnimation(name, animation) {
- return this.menuController.registerAnimation(name, animation);
- }
- isAnimating() {
- return this.menuController.isAnimating();
- }
- _getOpenSync() {
- return this.menuController._getOpenSync();
- }
- _createAnimation(type, menuCmp) {
- return this.menuController._createAnimation(type, menuCmp);
- }
- _register(menu) {
- return this.menuController._register(menu);
- }
- _unregister(menu) {
- return this.menuController._unregister(menu);
- }
- _setOpen(menu, shouldOpen, animated) {
- return this.menuController._setOpen(menu, shouldOpen, animated);
- }
- }
- class DomController {
- /**
- * Schedules a task to run during the READ phase of the next frame.
- * This task should only read the DOM, but never modify it.
- */
- read(cb) {
- getQueue().read(cb);
- }
- /**
- * Schedules a task to run during the WRITE phase of the next frame.
- * This task should write the DOM, but never READ it.
- */
- write(cb) {
- getQueue().write(cb);
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DomController, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
- /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DomController, providedIn: 'root' });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DomController, decorators: [{
- type: Injectable,
- args: [{
- providedIn: 'root',
- }]
- }] });
- const getQueue = () => {
- const win = typeof window !== 'undefined' ? window : null;
- if (win != null) {
- const Ionic = win.Ionic;
- if (Ionic?.queue) {
- return Ionic.queue;
- }
- return {
- read: (cb) => win.requestAnimationFrame(cb),
- write: (cb) => win.requestAnimationFrame(cb),
- };
- }
- return {
- read: (cb) => cb(),
- write: (cb) => cb(),
- };
- };
- class Platform {
- doc;
- _readyPromise;
- win;
- /**
- * @hidden
- */
- backButton = new Subject();
- /**
- * The keyboardDidShow event emits when the
- * on-screen keyboard is presented.
- */
- keyboardDidShow = new Subject();
- /**
- * The keyboardDidHide event emits when the
- * on-screen keyboard is hidden.
- */
- keyboardDidHide = new Subject();
- /**
- * The pause event emits when the native platform puts the application
- * into the background, typically when the user switches to a different
- * application. This event would emit when a Cordova app is put into
- * the background, however, it would not fire on a standard web browser.
- */
- pause = new Subject();
- /**
- * The resume event emits when the native platform pulls the application
- * out from the background. This event would emit when a Cordova app comes
- * out from the background, however, it would not fire on a standard web browser.
- */
- resume = new Subject();
- /**
- * The resize event emits when the browser window has changed dimensions. This
- * could be from a browser window being physically resized, or from a device
- * changing orientation.
- */
- resize = new Subject();
- constructor(doc, zone) {
- this.doc = doc;
- zone.run(() => {
- this.win = doc.defaultView;
- this.backButton.subscribeWithPriority = function (priority, callback) {
- return this.subscribe((ev) => {
- return ev.register(priority, (processNextHandler) => zone.run(() => callback(processNextHandler)));
- });
- };
- proxyEvent(this.pause, doc, 'pause', zone);
- proxyEvent(this.resume, doc, 'resume', zone);
- proxyEvent(this.backButton, doc, 'ionBackButton', zone);
- proxyEvent(this.resize, this.win, 'resize', zone);
- proxyEvent(this.keyboardDidShow, this.win, 'ionKeyboardDidShow', zone);
- proxyEvent(this.keyboardDidHide, this.win, 'ionKeyboardDidHide', zone);
- let readyResolve;
- this._readyPromise = new Promise((res) => {
- readyResolve = res;
- });
- if (this.win?.['cordova']) {
- doc.addEventListener('deviceready', () => {
- readyResolve('cordova');
- }, { once: true });
- }
- else {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- readyResolve('dom');
- }
- });
- }
- /**
- * @returns returns true/false based on platform.
- * @description
- * Depending on the platform the user is on, `is(platformName)` will
- * return `true` or `false`. Note that the same app can return `true`
- * for more than one platform name. For example, an app running from
- * an iPad would return `true` for the platform names: `mobile`,
- * `ios`, `ipad`, and `tablet`. Additionally, if the app was running
- * from Cordova then `cordova` would be true, and if it was running
- * from a web browser on the iPad then `mobileweb` would be `true`.
- *
- * ```
- * import { Platform } from 'ionic-angular';
- *
- * @Component({...})
- * export MyPage {
- * constructor(public platform: Platform) {
- * if (this.platform.is('ios')) {
- * // This will only print when on iOS
- * console.log('I am an iOS device!');
- * }
- * }
- * }
- * ```
- *
- * | Platform Name | Description |
- * |-----------------|------------------------------------|
- * | android | on a device running Android. |
- * | capacitor | on a device running Capacitor. |
- * | cordova | on a device running Cordova. |
- * | ios | on a device running iOS. |
- * | ipad | on an iPad device. |
- * | iphone | on an iPhone device. |
- * | phablet | on a phablet device. |
- * | tablet | on a tablet device. |
- * | electron | in Electron on a desktop device. |
- * | pwa | as a PWA app. |
- * | mobile | on a mobile device. |
- * | mobileweb | on a mobile device in a browser. |
- * | desktop | on a desktop device. |
- * | hybrid | is a cordova or capacitor app. |
- *
- */
- is(platformName) {
- return isPlatform(this.win, platformName);
- }
- /**
- * @returns the array of platforms
- * @description
- * Depending on what device you are on, `platforms` can return multiple values.
- * Each possible value is a hierarchy of platforms. For example, on an iPhone,
- * it would return `mobile`, `ios`, and `iphone`.
- *
- * ```
- * import { Platform } from 'ionic-angular';
- *
- * @Component({...})
- * export MyPage {
- * constructor(public platform: Platform) {
- * // This will print an array of the current platforms
- * console.log(this.platform.platforms());
- * }
- * }
- * ```
- */
- platforms() {
- return getPlatforms(this.win);
- }
- /**
- * Returns a promise when the platform is ready and native functionality
- * can be called. If the app is running from within a web browser, then
- * the promise will resolve when the DOM is ready. When the app is running
- * from an application engine such as Cordova, then the promise will
- * resolve when Cordova triggers the `deviceready` event.
- *
- * The resolved value is the `readySource`, which states which platform
- * ready was used. For example, when Cordova is ready, the resolved ready
- * source is `cordova`. The default ready source value will be `dom`. The
- * `readySource` is useful if different logic should run depending on the
- * platform the app is running from. For example, only Cordova can execute
- * the status bar plugin, so the web should not run status bar plugin logic.
- *
- * ```
- * import { Component } from '@angular/core';
- * import { Platform } from 'ionic-angular';
- *
- * @Component({...})
- * export MyApp {
- * constructor(public platform: Platform) {
- * this.platform.ready().then((readySource) => {
- * console.log('Platform ready from', readySource);
- * // Platform now ready, execute any required native code
- * });
- * }
- * }
- * ```
- */
- ready() {
- return this._readyPromise;
- }
- /**
- * Returns if this app is using right-to-left language direction or not.
- * We recommend the app's `index.html` file already has the correct `dir`
- * attribute value set, such as `<html dir="ltr">` or `<html dir="rtl">`.
- * [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
- */
- get isRTL() {
- return this.doc.dir === 'rtl';
- }
- /**
- * Get the query string parameter
- */
- getQueryParam(key) {
- return readQueryParam(this.win.location.href, key);
- }
- /**
- * Returns `true` if the app is in landscape mode.
- */
- isLandscape() {
- return !this.isPortrait();
- }
- /**
- * Returns `true` if the app is in portrait mode.
- */
- isPortrait() {
- return this.win.matchMedia?.('(orientation: portrait)').matches;
- }
- testUserAgent(expression) {
- const nav = this.win.navigator;
- return !!(nav?.userAgent && nav.userAgent.indexOf(expression) >= 0);
- }
- /**
- * Get the current url.
- */
- url() {
- return this.win.location.href;
- }
- /**
- * Gets the width of the platform's viewport using `window.innerWidth`.
- */
- width() {
- return this.win.innerWidth;
- }
- /**
- * Gets the height of the platform's viewport using `window.innerHeight`.
- */
- height() {
- return this.win.innerHeight;
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Platform, deps: [{ token: DOCUMENT }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
- /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Platform, providedIn: 'root' });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Platform, decorators: [{
- type: Injectable,
- args: [{
- providedIn: 'root',
- }]
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
- type: Inject,
- args: [DOCUMENT]
- }] }, { type: i0.NgZone }]; } });
- const readQueryParam = (url, key) => {
- key = key.replace(/[[\]\\]/g, '\\$&');
- const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
- const results = regex.exec(url);
- return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
- };
- const proxyEvent = (emitter, el, eventName, zone) => {
- if (el) {
- el.addEventListener(eventName, (ev) => {
- /**
- * `zone.run` is required to make sure that we are running inside the Angular zone
- * at all times. This is necessary since an app that has Capacitor will
- * override the `document.addEventListener` with its own implementation.
- * The override causes the event to no longer be in the Angular zone.
- */
- zone.run(() => {
- // ?? cordova might emit "null" events
- const value = ev != null ? ev.detail : undefined;
- emitter.next(value);
- });
- });
- }
- };
- class NavController {
- location;
- serializer;
- router;
- topOutlet;
- direction = DEFAULT_DIRECTION;
- animated = DEFAULT_ANIMATED;
- animationBuilder;
- guessDirection = 'forward';
- guessAnimation;
- lastNavId = -1;
- constructor(platform, location, serializer, router) {
- this.location = location;
- this.serializer = serializer;
- this.router = router;
- // Subscribe to router events to detect direction
- if (router) {
- router.events.subscribe((ev) => {
- if (ev instanceof NavigationStart) {
- // restoredState is set if the browser back/forward button is used
- const id = ev.restoredState ? ev.restoredState.navigationId : ev.id;
- this.guessDirection = this.guessAnimation = id < this.lastNavId ? 'back' : 'forward';
- this.lastNavId = this.guessDirection === 'forward' ? ev.id : id;
- }
- });
- }
- // Subscribe to backButton events
- platform.backButton.subscribeWithPriority(0, (processNextHandler) => {
- this.pop();
- processNextHandler();
- });
- }
- /**
- * This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
- * it's equivalent to calling `this.router.navigateByUrl()`, but it's explicit about the **direction** of the transition.
- *
- * Going **forward** means that a new page is going to be pushed to the stack of the outlet (ion-router-outlet),
- * and that it will show a "forward" animation by default.
- *
- * Navigating forward can also be triggered in a declarative manner by using the `[routerDirection]` directive:
- *
- * ```html
- * <a routerLink="/path/to/page" routerDirection="forward">Link</a>
- * ```
- */
- navigateForward(url, options = {}) {
- this.setDirection('forward', options.animated, options.animationDirection, options.animation);
- return this.navigate(url, options);
- }
- /**
- * This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
- * it's equivalent to calling:
- *
- * ```ts
- * this.navController.setDirection('back');
- * this.router.navigateByUrl(path);
- * ```
- *
- * Going **back** means that all the pages in the stack until the navigated page is found will be popped,
- * and that it will show a "back" animation by default.
- *
- * Navigating back can also be triggered in a declarative manner by using the `[routerDirection]` directive:
- *
- * ```html
- * <a routerLink="/path/to/page" routerDirection="back">Link</a>
- * ```
- */
- navigateBack(url, options = {}) {
- this.setDirection('back', options.animated, options.animationDirection, options.animation);
- return this.navigate(url, options);
- }
- /**
- * This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
- * it's equivalent to calling:
- *
- * ```ts
- * this.navController.setDirection('root');
- * this.router.navigateByUrl(path);
- * ```
- *
- * Going **root** means that all existing pages in the stack will be removed,
- * and the navigated page will become the single page in the stack.
- *
- * Navigating root can also be triggered in a declarative manner by using the `[routerDirection]` directive:
- *
- * ```html
- * <a routerLink="/path/to/page" routerDirection="root">Link</a>
- * ```
- */
- navigateRoot(url, options = {}) {
- this.setDirection('root', options.animated, options.animationDirection, options.animation);
- return this.navigate(url, options);
- }
- /**
- * Same as [Location](https://angular.io/api/common/Location)'s back() method.
- * It will use the standard `window.history.back()` under the hood, but featuring a `back` animation
- * by default.
- */
- back(options = { animated: true, animationDirection: 'back' }) {
- this.setDirection('back', options.animated, options.animationDirection, options.animation);
- return this.location.back();
- }
- /**
- * This methods goes back in the context of Ionic's stack navigation.
- *
- * It recursively finds the top active `ion-router-outlet` and calls `pop()`.
- * This is the recommended way to go back when you are using `ion-router-outlet`.
- *
- * Resolves to `true` if it was able to pop.
- */
- async pop() {
- let outlet = this.topOutlet;
- while (outlet) {
- if (await outlet.pop()) {
- return true;
- }
- else {
- outlet = outlet.parentOutlet;
- }
- }
- return false;
- }
- /**
- * This methods specifies the direction of the next navigation performed by the Angular router.
- *
- * `setDirection()` does not trigger any transition, it just sets some flags to be consumed by `ion-router-outlet`.
- *
- * It's recommended to use `navigateForward()`, `navigateBack()` and `navigateRoot()` instead of `setDirection()`.
- */
- setDirection(direction, animated, animationDirection, animationBuilder) {
- this.direction = direction;
- this.animated = getAnimation(direction, animated, animationDirection);
- this.animationBuilder = animationBuilder;
- }
- /**
- * @internal
- */
- setTopOutlet(outlet) {
- this.topOutlet = outlet;
- }
- /**
- * @internal
- */
- consumeTransition() {
- let direction = 'root';
- let animation;
- const animationBuilder = this.animationBuilder;
- if (this.direction === 'auto') {
- direction = this.guessDirection;
- animation = this.guessAnimation;
- }
- else {
- animation = this.animated;
- direction = this.direction;
- }
- this.direction = DEFAULT_DIRECTION;
- this.animated = DEFAULT_ANIMATED;
- this.animationBuilder = undefined;
- return {
- direction,
- animation,
- animationBuilder,
- };
- }
- navigate(url, options) {
- if (Array.isArray(url)) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.router.navigate(url, options);
- }
- else {
- /**
- * navigateByUrl ignores any properties that
- * would change the url, so things like queryParams
- * would be ignored unless we create a url tree
- * More Info: https://github.com/angular/angular/issues/18798
- */
- const urlTree = this.serializer.parse(url.toString());
- if (options.queryParams !== undefined) {
- urlTree.queryParams = { ...options.queryParams };
- }
- if (options.fragment !== undefined) {
- urlTree.fragment = options.fragment;
- }
- /**
- * `navigateByUrl` will still apply `NavigationExtras` properties
- * that do not modify the url, such as `replaceUrl` which is why
- * `options` is passed in here.
- */
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.router.navigateByUrl(urlTree, options);
- }
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavController, deps: [{ token: Platform }, { token: i1.Location }, { token: i3.UrlSerializer }, { token: i3.Router, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
- /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavController, providedIn: 'root' });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavController, decorators: [{
- type: Injectable,
- args: [{
- providedIn: 'root',
- }]
- }], ctorParameters: function () { return [{ type: Platform }, { type: i1.Location }, { type: i3.UrlSerializer }, { type: i3.Router, decorators: [{
- type: Optional
- }] }]; } });
- const getAnimation = (direction, animated, animationDirection) => {
- if (animated === false) {
- return undefined;
- }
- if (animationDirection !== undefined) {
- return animationDirection;
- }
- if (direction === 'forward' || direction === 'back') {
- return direction;
- }
- else if (direction === 'root' && animated === true) {
- return 'forward';
- }
- return undefined;
- };
- const DEFAULT_DIRECTION = 'auto';
- const DEFAULT_ANIMATED = undefined;
- class Config {
- get(key, fallback) {
- const c = getConfig();
- if (c) {
- return c.get(key, fallback);
- }
- return null;
- }
- getBoolean(key, fallback) {
- const c = getConfig();
- if (c) {
- return c.getBoolean(key, fallback);
- }
- return false;
- }
- getNumber(key, fallback) {
- const c = getConfig();
- if (c) {
- return c.getNumber(key, fallback);
- }
- return 0;
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Config, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
- /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Config, providedIn: 'root' });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: Config, decorators: [{
- type: Injectable,
- args: [{
- providedIn: 'root',
- }]
- }] });
- const ConfigToken = new InjectionToken('USERCONFIG');
- const getConfig = () => {
- if (typeof window !== 'undefined') {
- const Ionic = window.Ionic;
- if (Ionic?.config) {
- return Ionic.config;
- }
- }
- return null;
- };
- /**
- * @description
- * NavParams are an object that exists on a page and can contain data for that particular view.
- * Similar to how data was pass to a view in V1 with `$stateParams`, NavParams offer a much more flexible
- * option with a simple `get` method.
- *
- * @usage
- * ```ts
- * import { NavParams } from '@ionic/angular';
- *
- * export class MyClass{
- *
- * constructor(navParams: NavParams){
- * // userParams is an object we have in our nav-parameters
- * navParams.get('userParams');
- * }
- *
- * }
- * ```
- */
- class NavParams {
- data;
- constructor(data = {}) {
- this.data = data;
- console.warn(`[Ionic Warning]: NavParams has been deprecated in favor of using Angular's input API. Developers should migrate to either the @Input decorator or the Signals-based input API.`);
- }
- /**
- * Get the value of a nav-parameter for the current view
- *
- * ```ts
- * import { NavParams } from 'ionic-angular';
- *
- * export class MyClass{
- * constructor(public navParams: NavParams){
- * // userParams is an object we have in our nav-parameters
- * this.navParams.get('userParams');
- * }
- * }
- * ```
- *
- * @param param Which param you want to look up
- */
- get(param) {
- return this.data[param];
- }
- }
- // TODO(FW-2827): types
- class AngularDelegate {
- zone = inject(NgZone);
- applicationRef = inject(ApplicationRef);
- config = inject(ConfigToken);
- create(environmentInjector, injector, elementReferenceKey) {
- return new AngularFrameworkDelegate(environmentInjector, injector, this.applicationRef, this.zone, elementReferenceKey, this.config.useSetInputAPI ?? false);
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AngularDelegate, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
- /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AngularDelegate });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AngularDelegate, decorators: [{
- type: Injectable
- }] });
- class AngularFrameworkDelegate {
- environmentInjector;
- injector;
- applicationRef;
- zone;
- elementReferenceKey;
- enableSignalsSupport;
- elRefMap = new WeakMap();
- elEventsMap = new WeakMap();
- constructor(environmentInjector, injector, applicationRef, zone, elementReferenceKey, enableSignalsSupport) {
- this.environmentInjector = environmentInjector;
- this.injector = injector;
- this.applicationRef = applicationRef;
- this.zone = zone;
- this.elementReferenceKey = elementReferenceKey;
- this.enableSignalsSupport = enableSignalsSupport;
- }
- attachViewToDom(container, component, params, cssClasses) {
- return this.zone.run(() => {
- return new Promise((resolve) => {
- const componentProps = {
- ...params,
- };
- /**
- * Ionic Angular passes a reference to a modal
- * or popover that can be accessed using a
- * variable in the overlay component. If
- * elementReferenceKey is defined, then we should
- * pass a reference to the component using
- * elementReferenceKey as the key.
- */
- if (this.elementReferenceKey !== undefined) {
- componentProps[this.elementReferenceKey] = container;
- }
- const el = attachView(this.zone, this.environmentInjector, this.injector, this.applicationRef, this.elRefMap, this.elEventsMap, container, component, componentProps, cssClasses, this.elementReferenceKey, this.enableSignalsSupport);
- resolve(el);
- });
- });
- }
- removeViewFromDom(_container, component) {
- return this.zone.run(() => {
- return new Promise((resolve) => {
- const componentRef = this.elRefMap.get(component);
- if (componentRef) {
- componentRef.destroy();
- this.elRefMap.delete(component);
- const unbindEvents = this.elEventsMap.get(component);
- if (unbindEvents) {
- unbindEvents();
- this.elEventsMap.delete(component);
- }
- }
- resolve();
- });
- });
- }
- }
- const attachView = (zone, environmentInjector, injector, applicationRef, elRefMap, elEventsMap, container, component, params, cssClasses, elementReferenceKey, enableSignalsSupport) => {
- /**
- * Wraps the injector with a custom injector that
- * provides NavParams to the component.
- *
- * NavParams is a legacy feature from Ionic v3 that allows
- * Angular developers to provide data to a component
- * and access it by providing NavParams as a dependency
- * in the constructor.
- *
- * The modern approach is to access the data directly
- * from the component's class instance.
- */
- const childInjector = Injector.create({
- providers: getProviders(params),
- parent: injector,
- });
- const componentRef = createComponent(component, {
- environmentInjector,
- elementInjector: childInjector,
- });
- const instance = componentRef.instance;
- const hostElement = componentRef.location.nativeElement;
- if (params) {
- /**
- * For modals and popovers, a reference to the component is
- * added to `params` during the call to attachViewToDom. If
- * a reference using this name is already set, this means
- * the app is trying to use the name as a component prop,
- * which will cause collisions.
- */
- if (elementReferenceKey && instance[elementReferenceKey] !== undefined) {
- console.error(`[Ionic Error]: ${elementReferenceKey} is a reserved property when using ${container.tagName.toLowerCase()}. Rename or remove the "${elementReferenceKey}" property from ${component.name}.`);
- }
- /**
- * Angular 14.1 added support for setInput
- * so we need to fall back to Object.assign
- * for Angular 14.0.
- */
- if (enableSignalsSupport === true && componentRef.setInput !== undefined) {
- const { modal, popover, ...otherParams } = params;
- /**
- * Any key/value pairs set in componentProps
- * must be set as inputs on the component instance.
- */
- for (const key in otherParams) {
- componentRef.setInput(key, otherParams[key]);
- }
- /**
- * Using setInput will cause an error when
- * setting modal/popover on a component that
- * does not define them as an input. For backwards
- * compatibility purposes we fall back to using
- * Object.assign for these properties.
- */
- if (modal !== undefined) {
- Object.assign(instance, { modal });
- }
- if (popover !== undefined) {
- Object.assign(instance, { popover });
- }
- }
- else {
- Object.assign(instance, params);
- }
- }
- if (cssClasses) {
- for (const cssClass of cssClasses) {
- hostElement.classList.add(cssClass);
- }
- }
- const unbindEvents = bindLifecycleEvents(zone, instance, hostElement);
- container.appendChild(hostElement);
- applicationRef.attachView(componentRef.hostView);
- elRefMap.set(hostElement, componentRef);
- elEventsMap.set(hostElement, unbindEvents);
- return hostElement;
- };
- const LIFECYCLES = [
- LIFECYCLE_WILL_ENTER,
- LIFECYCLE_DID_ENTER,
- LIFECYCLE_WILL_LEAVE,
- LIFECYCLE_DID_LEAVE,
- LIFECYCLE_WILL_UNLOAD,
- ];
- const bindLifecycleEvents = (zone, instance, element) => {
- return zone.run(() => {
- const unregisters = LIFECYCLES.filter((eventName) => typeof instance[eventName] === 'function').map((eventName) => {
- const handler = (ev) => instance[eventName](ev.detail);
- element.addEventListener(eventName, handler);
- return () => element.removeEventListener(eventName, handler);
- });
- return () => unregisters.forEach((fn) => fn());
- });
- };
- const NavParamsToken = new InjectionToken('NavParamsToken');
- const getProviders = (params) => {
- return [
- {
- provide: NavParamsToken,
- useValue: params,
- },
- {
- provide: NavParams,
- useFactory: provideNavParamsInjectable,
- deps: [NavParamsToken],
- },
- ];
- };
- const provideNavParamsInjectable = (params) => {
- return new NavParams(params);
- };
- // TODO: Is there a way we can grab this from angular-component-lib instead?
- /* eslint-disable */
- /* tslint:disable */
- const proxyInputs = (Cmp, inputs) => {
- const Prototype = Cmp.prototype;
- inputs.forEach((item) => {
- Object.defineProperty(Prototype, item, {
- get() {
- return this.el[item];
- },
- set(val) {
- this.z.runOutsideAngular(() => (this.el[item] = val));
- },
- });
- });
- };
- const proxyMethods = (Cmp, methods) => {
- const Prototype = Cmp.prototype;
- methods.forEach((methodName) => {
- Prototype[methodName] = function () {
- const args = arguments;
- return this.z.runOutsideAngular(() => this.el[methodName].apply(this.el, args));
- };
- });
- };
- const proxyOutputs = (instance, el, events) => {
- events.forEach((eventName) => (instance[eventName] = fromEvent(el, eventName)));
- };
- // tslint:disable-next-line: only-arrow-functions
- function ProxyCmp(opts) {
- const decorator = function (cls) {
- const { defineCustomElementFn, inputs, methods } = opts;
- if (defineCustomElementFn !== undefined) {
- defineCustomElementFn();
- }
- if (inputs) {
- proxyInputs(cls, inputs);
- }
- if (methods) {
- proxyMethods(cls, methods);
- }
- return cls;
- };
- return decorator;
- }
- const POPOVER_INPUTS = [
- 'alignment',
- 'animated',
- 'arrow',
- 'keepContentsMounted',
- 'backdropDismiss',
- 'cssClass',
- 'dismissOnSelect',
- 'enterAnimation',
- 'event',
- 'focusTrap',
- 'isOpen',
- 'keyboardClose',
- 'leaveAnimation',
- 'mode',
- 'showBackdrop',
- 'translucent',
- 'trigger',
- 'triggerAction',
- 'reference',
- 'size',
- 'side',
- ];
- const POPOVER_METHODS = ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'];
- let IonPopover = class IonPopover {
- z;
- // TODO(FW-2827): type
- template;
- isCmpOpen = false;
- el;
- constructor(c, r, z) {
- this.z = z;
- this.el = r.nativeElement;
- this.el.addEventListener('ionMount', () => {
- this.isCmpOpen = true;
- c.detectChanges();
- });
- this.el.addEventListener('didDismiss', () => {
- this.isCmpOpen = false;
- c.detectChanges();
- });
- proxyOutputs(this, this.el, [
- 'ionPopoverDidPresent',
- 'ionPopoverWillPresent',
- 'ionPopoverWillDismiss',
- 'ionPopoverDidDismiss',
- 'didPresent',
- 'willPresent',
- 'willDismiss',
- 'didDismiss',
- ]);
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonPopover, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: IonPopover, selector: "ion-popover", inputs: { alignment: "alignment", animated: "animated", arrow: "arrow", keepContentsMounted: "keepContentsMounted", backdropDismiss: "backdropDismiss", cssClass: "cssClass", dismissOnSelect: "dismissOnSelect", enterAnimation: "enterAnimation", event: "event", focusTrap: "focusTrap", isOpen: "isOpen", keyboardClose: "keyboardClose", leaveAnimation: "leaveAnimation", mode: "mode", showBackdrop: "showBackdrop", translucent: "translucent", trigger: "trigger", triggerAction: "triggerAction", reference: "reference", size: "size", side: "side" }, queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0 });
- };
- IonPopover = __decorate([
- ProxyCmp({
- inputs: POPOVER_INPUTS,
- methods: POPOVER_METHODS,
- })
- /**
- * @Component extends from @Directive
- * so by defining the inputs here we
- * do not need to re-define them for the
- * lazy loaded popover.
- */
- ], IonPopover);
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonPopover, decorators: [{
- type: Directive,
- args: [{
- selector: 'ion-popover',
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: POPOVER_INPUTS,
- }]
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { template: [{
- type: ContentChild,
- args: [TemplateRef, { static: false }]
- }] } });
- const MODAL_INPUTS = [
- 'animated',
- 'keepContentsMounted',
- 'backdropBreakpoint',
- 'backdropDismiss',
- 'breakpoints',
- 'canDismiss',
- 'cssClass',
- 'enterAnimation',
- 'expandToScroll',
- 'event',
- 'focusTrap',
- 'handle',
- 'handleBehavior',
- 'initialBreakpoint',
- 'isOpen',
- 'keyboardClose',
- 'leaveAnimation',
- 'mode',
- 'presentingElement',
- 'showBackdrop',
- 'translucent',
- 'trigger',
- ];
- const MODAL_METHODS = [
- 'present',
- 'dismiss',
- 'onDidDismiss',
- 'onWillDismiss',
- 'setCurrentBreakpoint',
- 'getCurrentBreakpoint',
- ];
- let IonModal = class IonModal {
- z;
- // TODO(FW-2827): type
- template;
- isCmpOpen = false;
- el;
- constructor(c, r, z) {
- this.z = z;
- this.el = r.nativeElement;
- this.el.addEventListener('ionMount', () => {
- this.isCmpOpen = true;
- c.detectChanges();
- });
- this.el.addEventListener('didDismiss', () => {
- this.isCmpOpen = false;
- c.detectChanges();
- });
- proxyOutputs(this, this.el, [
- 'ionModalDidPresent',
- 'ionModalWillPresent',
- 'ionModalWillDismiss',
- 'ionModalDidDismiss',
- 'ionBreakpointDidChange',
- 'didPresent',
- 'willPresent',
- 'willDismiss',
- 'didDismiss',
- ]);
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonModal, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: IonModal, selector: "ion-modal", inputs: { animated: "animated", keepContentsMounted: "keepContentsMounted", backdropBreakpoint: "backdropBreakpoint", backdropDismiss: "backdropDismiss", breakpoints: "breakpoints", canDismiss: "canDismiss", cssClass: "cssClass", enterAnimation: "enterAnimation", expandToScroll: "expandToScroll", event: "event", focusTrap: "focusTrap", handle: "handle", handleBehavior: "handleBehavior", initialBreakpoint: "initialBreakpoint", isOpen: "isOpen", keyboardClose: "keyboardClose", leaveAnimation: "leaveAnimation", mode: "mode", presentingElement: "presentingElement", showBackdrop: "showBackdrop", translucent: "translucent", trigger: "trigger" }, queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0 });
- };
- IonModal = __decorate([
- ProxyCmp({
- inputs: MODAL_INPUTS,
- methods: MODAL_METHODS,
- })
- /**
- * @Component extends from @Directive
- * so by defining the inputs here we
- * do not need to re-define them for the
- * lazy loaded popover.
- */
- ], IonModal);
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonModal, decorators: [{
- type: Directive,
- args: [{
- selector: 'ion-modal',
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: MODAL_INPUTS,
- }]
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { template: [{
- type: ContentChild,
- args: [TemplateRef, { static: false }]
- }] } });
- const insertView = (views, view, direction) => {
- if (direction === 'root') {
- return setRoot(views, view);
- }
- else if (direction === 'forward') {
- return setForward(views, view);
- }
- else {
- return setBack(views, view);
- }
- };
- const setRoot = (views, view) => {
- views = views.filter((v) => v.stackId !== view.stackId);
- views.push(view);
- return views;
- };
- const setForward = (views, view) => {
- const index = views.indexOf(view);
- if (index >= 0) {
- views = views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
- }
- else {
- views.push(view);
- }
- return views;
- };
- const setBack = (views, view) => {
- const index = views.indexOf(view);
- if (index >= 0) {
- return views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
- }
- else {
- return setRoot(views, view);
- }
- };
- const getUrl = (router, activatedRoute) => {
- const urlTree = router.createUrlTree(['.'], { relativeTo: activatedRoute });
- return router.serializeUrl(urlTree);
- };
- const isTabSwitch = (enteringView, leavingView) => {
- if (!leavingView) {
- return true;
- }
- return enteringView.stackId !== leavingView.stackId;
- };
- const computeStackId = (prefixUrl, url) => {
- if (!prefixUrl) {
- return undefined;
- }
- const segments = toSegments(url);
- for (let i = 0; i < segments.length; i++) {
- if (i >= prefixUrl.length) {
- return segments[i];
- }
- if (segments[i] !== prefixUrl[i]) {
- return undefined;
- }
- }
- return undefined;
- };
- const toSegments = (path) => {
- return path
- .split('/')
- .map((s) => s.trim())
- .filter((s) => s !== '');
- };
- const destroyView = (view) => {
- if (view) {
- view.ref.destroy();
- view.unlistenEvents();
- }
- };
- // TODO(FW-2827): types
- class StackController {
- containerEl;
- router;
- navCtrl;
- zone;
- location;
- views = [];
- runningTask;
- skipTransition = false;
- tabsPrefix;
- activeView;
- nextId = 0;
- constructor(tabsPrefix, containerEl, router, navCtrl, zone, location) {
- this.containerEl = containerEl;
- this.router = router;
- this.navCtrl = navCtrl;
- this.zone = zone;
- this.location = location;
- this.tabsPrefix = tabsPrefix !== undefined ? toSegments(tabsPrefix) : undefined;
- }
- createView(ref, activatedRoute) {
- const url = getUrl(this.router, activatedRoute);
- const element = ref?.location?.nativeElement;
- const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
- return {
- id: this.nextId++,
- stackId: computeStackId(this.tabsPrefix, url),
- unlistenEvents,
- element,
- ref,
- url,
- };
- }
- getExistingView(activatedRoute) {
- const activatedUrlKey = getUrl(this.router, activatedRoute);
- const view = this.views.find((vw) => vw.url === activatedUrlKey);
- if (view) {
- view.ref.changeDetectorRef.reattach();
- }
- return view;
- }
- setActive(enteringView) {
- const consumeResult = this.navCtrl.consumeTransition();
- let { direction, animation, animationBuilder } = consumeResult;
- const leavingView = this.activeView;
- const tabSwitch = isTabSwitch(enteringView, leavingView);
- if (tabSwitch) {
- direction = 'back';
- animation = undefined;
- }
- const viewsSnapshot = this.views.slice();
- let currentNavigation;
- const router = this.router;
- // Angular >= 7.2.0
- if (router.getCurrentNavigation) {
- currentNavigation = router.getCurrentNavigation();
- // Angular < 7.2.0
- }
- else if (router.navigations?.value) {
- currentNavigation = router.navigations.value;
- }
- /**
- * If the navigation action
- * sets `replaceUrl: true`
- * then we need to make sure
- * we remove the last item
- * from our views stack
- */
- if (currentNavigation?.extras?.replaceUrl) {
- if (this.views.length > 0) {
- this.views.splice(-1, 1);
- }
- }
- const reused = this.views.includes(enteringView);
- const views = this.insertView(enteringView, direction);
- // Trigger change detection before transition starts
- // This will call ngOnInit() the first time too, just after the view
- // was attached to the dom, but BEFORE the transition starts
- if (!reused) {
- enteringView.ref.changeDetectorRef.detectChanges();
- }
- /**
- * If we are going back from a page that
- * was presented using a custom animation
- * we should default to using that
- * unless the developer explicitly
- * provided another animation.
- */
- const customAnimation = enteringView.animationBuilder;
- if (animationBuilder === undefined && direction === 'back' && !tabSwitch && customAnimation !== undefined) {
- animationBuilder = customAnimation;
- }
- /**
- * Save any custom animation so that navigating
- * back will use this custom animation by default.
- */
- if (leavingView) {
- leavingView.animationBuilder = animationBuilder;
- }
- // Wait until previous transitions finish
- return this.zone.runOutsideAngular(() => {
- return this.wait(() => {
- // disconnect leaving page from change detection to
- // reduce jank during the page transition
- if (leavingView) {
- leavingView.ref.changeDetectorRef.detach();
- }
- // In case the enteringView is the same as the leavingPage we need to reattach()
- enteringView.ref.changeDetectorRef.reattach();
- return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false, animationBuilder)
- .then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location, this.zone))
- .then(() => ({
- enteringView,
- direction,
- animation,
- tabSwitch,
- }));
- });
- });
- }
- canGoBack(deep, stackId = this.getActiveStackId()) {
- return this.getStack(stackId).length > deep;
- }
- pop(deep, stackId = this.getActiveStackId()) {
- return this.zone.run(() => {
- const views = this.getStack(stackId);
- if (views.length <= deep) {
- return Promise.resolve(false);
- }
- const view = views[views.length - deep - 1];
- let url = view.url;
- const viewSavedData = view.savedData;
- if (viewSavedData) {
- const primaryOutlet = viewSavedData.get('primary');
- if (primaryOutlet?.route?._routerState?.snapshot.url) {
- url = primaryOutlet.route._routerState.snapshot.url;
- }
- }
- const { animationBuilder } = this.navCtrl.consumeTransition();
- return this.navCtrl.navigateBack(url, { ...view.savedExtras, animation: animationBuilder }).then(() => true);
- });
- }
- startBackTransition() {
- const leavingView = this.activeView;
- if (leavingView) {
- const views = this.getStack(leavingView.stackId);
- const enteringView = views[views.length - 2];
- const customAnimation = enteringView.animationBuilder;
- return this.wait(() => {
- return this.transition(enteringView, // entering view
- leavingView, // leaving view
- 'back', this.canGoBack(2), true, customAnimation);
- });
- }
- return Promise.resolve();
- }
- endBackTransition(shouldComplete) {
- if (shouldComplete) {
- this.skipTransition = true;
- this.pop(1);
- }
- else if (this.activeView) {
- cleanup(this.activeView, this.views, this.views, this.location, this.zone);
- }
- }
- getLastUrl(stackId) {
- const views = this.getStack(stackId);
- return views.length > 0 ? views[views.length - 1] : undefined;
- }
- /**
- * @internal
- */
- getRootUrl(stackId) {
- const views = this.getStack(stackId);
- return views.length > 0 ? views[0] : undefined;
- }
- getActiveStackId() {
- return this.activeView ? this.activeView.stackId : undefined;
- }
- /**
- * @internal
- */
- getActiveView() {
- return this.activeView;
- }
- hasRunningTask() {
- return this.runningTask !== undefined;
- }
- destroy() {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- this.containerEl = undefined;
- this.views.forEach(destroyView);
- this.activeView = undefined;
- this.views = [];
- }
- getStack(stackId) {
- return this.views.filter((v) => v.stackId === stackId);
- }
- insertView(enteringView, direction) {
- this.activeView = enteringView;
- this.views = insertView(this.views, enteringView, direction);
- return this.views.slice();
- }
- transition(enteringView, leavingView, direction, showGoBack, progressAnimation, animationBuilder) {
- if (this.skipTransition) {
- this.skipTransition = false;
- return Promise.resolve(false);
- }
- if (leavingView === enteringView) {
- return Promise.resolve(false);
- }
- const enteringEl = enteringView ? enteringView.element : undefined;
- const leavingEl = leavingView ? leavingView.element : undefined;
- const containerEl = this.containerEl;
- if (enteringEl && enteringEl !== leavingEl) {
- enteringEl.classList.add('ion-page');
- enteringEl.classList.add('ion-page-invisible');
- if (containerEl.commit) {
- return containerEl.commit(enteringEl, leavingEl, {
- duration: direction === undefined ? 0 : undefined,
- direction,
- showGoBack,
- progressAnimation,
- animationBuilder,
- });
- }
- }
- return Promise.resolve(false);
- }
- async wait(task) {
- if (this.runningTask !== undefined) {
- await this.runningTask;
- this.runningTask = undefined;
- }
- const promise = (this.runningTask = task());
- promise.finally(() => (this.runningTask = undefined));
- return promise;
- }
- }
- const cleanupAsync = (activeRoute, views, viewsSnapshot, location, zone) => {
- if (typeof requestAnimationFrame === 'function') {
- return new Promise((resolve) => {
- requestAnimationFrame(() => {
- cleanup(activeRoute, views, viewsSnapshot, location, zone);
- resolve();
- });
- });
- }
- return Promise.resolve();
- };
- const cleanup = (activeRoute, views, viewsSnapshot, location, zone) => {
- /**
- * Re-enter the Angular zone when destroying page components. This will allow
- * lifecycle events (`ngOnDestroy`) to be run inside the Angular zone.
- */
- zone.run(() => viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView));
- views.forEach((view) => {
- /**
- * In the event that a user navigated multiple
- * times in rapid succession, we want to make sure
- * we don't pre-emptively detach a view while
- * it is in mid-transition.
- *
- * In this instance we also do not care about query
- * params or fragments as it will be the same view regardless
- */
- const locationWithoutParams = location.path().split('?')[0];
- const locationWithoutFragment = locationWithoutParams.split('#')[0];
- if (view !== activeRoute && view.url !== locationWithoutFragment) {
- const element = view.element;
- element.setAttribute('aria-hidden', 'true');
- element.classList.add('ion-page-hidden');
- view.ref.changeDetectorRef.detach();
- }
- });
- };
- // TODO(FW-2827): types
- // eslint-disable-next-line @angular-eslint/directive-class-suffix
- class IonRouterOutlet {
- parentOutlet;
- nativeEl;
- activatedView = null;
- tabsPrefix;
- _swipeGesture;
- stackCtrl;
- // Maintain map of activated route proxies for each component instance
- proxyMap = new WeakMap();
- // Keep the latest activated route in a subject for the proxy routes to switch map to
- currentActivatedRoute$ = new BehaviorSubject(null);
- activated = null;
- /** @internal */
- get activatedComponentRef() {
- return this.activated;
- }
- _activatedRoute = null;
- /**
- * The name of the outlet
- */
- name = PRIMARY_OUTLET;
- /** @internal */
- stackWillChange = new EventEmitter();
- /** @internal */
- stackDidChange = new EventEmitter();
- // eslint-disable-next-line @angular-eslint/no-output-rename
- activateEvents = new EventEmitter();
- // eslint-disable-next-line @angular-eslint/no-output-rename
- deactivateEvents = new EventEmitter();
- parentContexts = inject(ChildrenOutletContexts);
- location = inject(ViewContainerRef);
- environmentInjector = inject(EnvironmentInjector);
- inputBinder = inject(INPUT_BINDER, { optional: true });
- /** @nodoc */
- supportsBindingToComponentInputs = true;
- // Ionic providers
- config = inject(Config);
- navCtrl = inject(NavController);
- set animation(animation) {
- this.nativeEl.animation = animation;
- }
- set animated(animated) {
- this.nativeEl.animated = animated;
- }
- set swipeGesture(swipe) {
- this._swipeGesture = swipe;
- this.nativeEl.swipeHandler = swipe
- ? {
- canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(),
- onStart: () => this.stackCtrl.startBackTransition(),
- onEnd: (shouldContinue) => this.stackCtrl.endBackTransition(shouldContinue),
- }
- : undefined;
- }
- constructor(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet) {
- this.parentOutlet = parentOutlet;
- this.nativeEl = elementRef.nativeElement;
- this.name = name || PRIMARY_OUTLET;
- this.tabsPrefix = tabs === 'true' ? getUrl(router, activatedRoute) : undefined;
- this.stackCtrl = new StackController(this.tabsPrefix, this.nativeEl, router, this.navCtrl, zone, commonLocation);
- this.parentContexts.onChildOutletCreated(this.name, this);
- }
- ngOnDestroy() {
- this.stackCtrl.destroy();
- this.inputBinder?.unsubscribeFromRouteData(this);
- }
- getContext() {
- return this.parentContexts.getContext(this.name);
- }
- ngOnInit() {
- this.initializeOutletWithName();
- }
- // Note: Ionic deviates from the Angular Router implementation here
- initializeOutletWithName() {
- if (!this.activated) {
- // If the outlet was not instantiated at the time the route got activated we need to populate
- // the outlet when it is initialized (ie inside a NgIf)
- const context = this.getContext();
- if (context?.route) {
- this.activateWith(context.route, context.injector);
- }
- }
- new Promise((resolve) => componentOnReady(this.nativeEl, resolve)).then(() => {
- if (this._swipeGesture === undefined) {
- this.swipeGesture = this.config.getBoolean('swipeBackEnabled', this.nativeEl.mode === 'ios');
- }
- });
- }
- get isActivated() {
- return !!this.activated;
- }
- get component() {
- if (!this.activated) {
- throw new Error('Outlet is not activated');
- }
- return this.activated.instance;
- }
- get activatedRoute() {
- if (!this.activated) {
- throw new Error('Outlet is not activated');
- }
- return this._activatedRoute;
- }
- get activatedRouteData() {
- if (this._activatedRoute) {
- return this._activatedRoute.snapshot.data;
- }
- return {};
- }
- /**
- * Called when the `RouteReuseStrategy` instructs to detach the subtree
- */
- detach() {
- throw new Error('incompatible reuse strategy');
- }
- /**
- * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
- */
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- attach(_ref, _activatedRoute) {
- throw new Error('incompatible reuse strategy');
- }
- deactivate() {
- if (this.activated) {
- if (this.activatedView) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const context = this.getContext();
- this.activatedView.savedData = new Map(context.children['contexts']);
- /**
- * Angular v11.2.10 introduced a change
- * where this route context is cleared out when
- * a router-outlet is deactivated, However,
- * we need this route information in order to
- * return a user back to the correct tab when
- * leaving and then going back to the tab context.
- */
- const primaryOutlet = this.activatedView.savedData.get('primary');
- if (primaryOutlet && context.route) {
- primaryOutlet.route = { ...context.route };
- }
- /**
- * Ensure we are saving the NavigationExtras
- * data otherwise it will be lost
- */
- this.activatedView.savedExtras = {};
- if (context.route) {
- const contextSnapshot = context.route.snapshot;
- this.activatedView.savedExtras.queryParams = contextSnapshot.queryParams;
- this.activatedView.savedExtras.fragment = contextSnapshot.fragment;
- }
- }
- const c = this.component;
- this.activatedView = null;
- this.activated = null;
- this._activatedRoute = null;
- this.deactivateEvents.emit(c);
- }
- }
- activateWith(activatedRoute, environmentInjector) {
- if (this.isActivated) {
- throw new Error('Cannot activate an already activated outlet');
- }
- this._activatedRoute = activatedRoute;
- let cmpRef;
- let enteringView = this.stackCtrl.getExistingView(activatedRoute);
- if (enteringView) {
- cmpRef = this.activated = enteringView.ref;
- const saved = enteringView.savedData;
- if (saved) {
- // self-restore
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const context = this.getContext();
- context.children['contexts'] = saved;
- }
- // Updated activated route proxy for this component
- this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute);
- }
- else {
- const snapshot = activatedRoute._futureSnapshot;
- /**
- * Angular 14 introduces a new `loadComponent` property to the route config.
- * This function will assign a `component` property to the route snapshot.
- * We check for the presence of this property to determine if the route is
- * using standalone components.
- */
- const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
- // We create an activated route proxy object that will maintain future updates for this component
- // over its lifecycle in the stack.
- const component$ = new BehaviorSubject(null);
- const activatedRouteProxy = this.createActivatedRouteProxy(component$, activatedRoute);
- const injector = new OutletInjector(activatedRouteProxy, childContexts, this.location.injector);
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const component = snapshot.routeConfig.component ?? snapshot.component;
- /**
- * View components need to be added as a child of ion-router-outlet
- * for page transitions and swipe to go back.
- * However, createComponent mounts components as siblings of the
- * ViewContainerRef. As a result, outletContent must reference
- * an ng-container inside of ion-router-outlet and not
- * ion-router-outlet itself.
- */
- cmpRef = this.activated = this.outletContent.createComponent(component, {
- index: this.outletContent.length,
- injector,
- environmentInjector: environmentInjector ?? this.environmentInjector,
- });
- // Once the component is created we can push it to our local subject supplied to the proxy
- component$.next(cmpRef.instance);
- // Calling `markForCheck` to make sure we will run the change detection when the
- // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
- /**
- * At this point this.activated has been set earlier
- * in this function, so it is guaranteed to be non-null.
- */
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- enteringView = this.stackCtrl.createView(this.activated, activatedRoute);
- // Store references to the proxy by component
- this.proxyMap.set(cmpRef.instance, activatedRouteProxy);
- this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute });
- }
- this.inputBinder?.bindActivatedRouteToOutletComponent(this);
- this.activatedView = enteringView;
- /**
- * The top outlet is set prior to the entering view's transition completing,
- * so that when we have nested outlets (e.g. ion-tabs inside an ion-router-outlet),
- * the tabs outlet will be assigned as the top outlet when a view inside tabs is
- * activated.
- *
- * In this scenario, activeWith is called for both the tabs and the root router outlet.
- * To avoid a race condition, we assign the top outlet synchronously.
- */
- this.navCtrl.setTopOutlet(this);
- const leavingView = this.stackCtrl.getActiveView();
- this.stackWillChange.emit({
- enteringView,
- tabSwitch: isTabSwitch(enteringView, leavingView),
- });
- this.stackCtrl.setActive(enteringView).then((data) => {
- this.activateEvents.emit(cmpRef.instance);
- this.stackDidChange.emit(data);
- });
- }
- /**
- * Returns `true` if there are pages in the stack to go back.
- */
- canGoBack(deep = 1, stackId) {
- return this.stackCtrl.canGoBack(deep, stackId);
- }
- /**
- * Resolves to `true` if it the outlet was able to sucessfully pop the last N pages.
- */
- pop(deep = 1, stackId) {
- return this.stackCtrl.pop(deep, stackId);
- }
- /**
- * Returns the URL of the active page of each stack.
- */
- getLastUrl(stackId) {
- const active = this.stackCtrl.getLastUrl(stackId);
- return active ? active.url : undefined;
- }
- /**
- * Returns the RouteView of the active page of each stack.
- * @internal
- */
- getLastRouteView(stackId) {
- return this.stackCtrl.getLastUrl(stackId);
- }
- /**
- * Returns the root view in the tab stack.
- * @internal
- */
- getRootView(stackId) {
- return this.stackCtrl.getRootUrl(stackId);
- }
- /**
- * Returns the active stack ID. In the context of ion-tabs, it means the active tab.
- */
- getActiveStackId() {
- return this.stackCtrl.getActiveStackId();
- }
- /**
- * Since the activated route can change over the life time of a component in an ion router outlet, we create
- * a proxy so that we can update the values over time as a user navigates back to components already in the stack.
- */
- createActivatedRouteProxy(component$, activatedRoute) {
- const proxy = new ActivatedRoute();
- proxy._futureSnapshot = activatedRoute._futureSnapshot;
- proxy._routerState = activatedRoute._routerState;
- proxy.snapshot = activatedRoute.snapshot;
- proxy.outlet = activatedRoute.outlet;
- proxy.component = activatedRoute.component;
- // Setup wrappers for the observables so consumers don't have to worry about switching to new observables as the state updates
- proxy._paramMap = this.proxyObservable(component$, 'paramMap');
- proxy._queryParamMap = this.proxyObservable(component$, 'queryParamMap');
- proxy.url = this.proxyObservable(component$, 'url');
- proxy.params = this.proxyObservable(component$, 'params');
- proxy.queryParams = this.proxyObservable(component$, 'queryParams');
- proxy.fragment = this.proxyObservable(component$, 'fragment');
- proxy.data = this.proxyObservable(component$, 'data');
- return proxy;
- }
- /**
- * Create a wrapped observable that will switch to the latest activated route matched by the given component
- */
- proxyObservable(component$, path) {
- return component$.pipe(
- // First wait until the component instance is pushed
- filter((component) => !!component), switchMap((component) => this.currentActivatedRoute$.pipe(filter((current) => current !== null && current.component === component), switchMap((current) => current && current.activatedRoute[path]), distinctUntilChanged())));
- }
- /**
- * Updates the activated route proxy for the given component to the new incoming router state
- */
- updateActivatedRouteProxy(component, activatedRoute) {
- const proxy = this.proxyMap.get(component);
- if (!proxy) {
- throw new Error(`Could not find activated route proxy for view`);
- }
- proxy._futureSnapshot = activatedRoute._futureSnapshot;
- proxy._routerState = activatedRoute._routerState;
- proxy.snapshot = activatedRoute.snapshot;
- proxy.outlet = activatedRoute.outlet;
- proxy.component = activatedRoute.component;
- this.currentActivatedRoute$.next({ component, activatedRoute });
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonRouterOutlet, deps: [{ token: 'name', attribute: true }, { token: 'tabs', attribute: true, optional: true }, { token: i1.Location }, { token: i0.ElementRef }, { token: i3.Router }, { token: i0.NgZone }, { token: i3.ActivatedRoute }, { token: IonRouterOutlet, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: IonRouterOutlet, selector: "ion-router-outlet", inputs: { animated: "animated", animation: "animation", mode: "mode", swipeGesture: "swipeGesture", name: "name" }, outputs: { stackWillChange: "stackWillChange", stackDidChange: "stackDidChange", activateEvents: "activate", deactivateEvents: "deactivate" }, exportAs: ["outlet"], ngImport: i0 });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonRouterOutlet, decorators: [{
- type: Directive,
- args: [{
- selector: 'ion-router-outlet',
- exportAs: 'outlet',
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['animated', 'animation', 'mode', 'swipeGesture'],
- }]
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
- type: Attribute,
- args: ['name']
- }] }, { type: undefined, decorators: [{
- type: Optional
- }, {
- type: Attribute,
- args: ['tabs']
- }] }, { type: i1.Location }, { type: i0.ElementRef }, { type: i3.Router }, { type: i0.NgZone }, { type: i3.ActivatedRoute }, { type: IonRouterOutlet, decorators: [{
- type: SkipSelf
- }, {
- type: Optional
- }] }]; }, propDecorators: { name: [{
- type: Input
- }], stackWillChange: [{
- type: Output
- }], stackDidChange: [{
- type: Output
- }], activateEvents: [{
- type: Output,
- args: ['activate']
- }], deactivateEvents: [{
- type: Output,
- args: ['deactivate']
- }] } });
- class OutletInjector {
- route;
- childContexts;
- parent;
- constructor(route, childContexts, parent) {
- this.route = route;
- this.childContexts = childContexts;
- this.parent = parent;
- }
- get(token, notFoundValue) {
- if (token === ActivatedRoute) {
- return this.route;
- }
- if (token === ChildrenOutletContexts) {
- return this.childContexts;
- }
- return this.parent.get(token, notFoundValue);
- }
- }
- // TODO: FW-4785 - Remove this once Angular 15 support is dropped
- const INPUT_BINDER = new InjectionToken('');
- /**
- * Injectable used as a tree-shakable provider for opting in to binding router data to component
- * inputs.
- *
- * The RouterOutlet registers itself with this service when an `ActivatedRoute` is attached or
- * activated. When this happens, the service subscribes to the `ActivatedRoute` observables (params,
- * queryParams, data) and sets the inputs of the component using `ComponentRef.setInput`.
- * Importantly, when an input does not have an item in the route data with a matching key, this
- * input is set to `undefined`. If it were not done this way, the previous information would be
- * retained if the data got removed from the route (i.e. if a query parameter is removed).
- *
- * The `RouterOutlet` should unregister itself when destroyed via `unsubscribeFromRouteData` so that
- * the subscriptions are cleaned up.
- */
- class RoutedComponentInputBinder {
- outletDataSubscriptions = new Map();
- bindActivatedRouteToOutletComponent(outlet) {
- this.unsubscribeFromRouteData(outlet);
- this.subscribeToRouteData(outlet);
- }
- unsubscribeFromRouteData(outlet) {
- this.outletDataSubscriptions.get(outlet)?.unsubscribe();
- this.outletDataSubscriptions.delete(outlet);
- }
- subscribeToRouteData(outlet) {
- const { activatedRoute } = outlet;
- const dataSubscription = combineLatest([activatedRoute.queryParams, activatedRoute.params, activatedRoute.data])
- .pipe(switchMap(([queryParams, params, data], index) => {
- data = { ...queryParams, ...params, ...data };
- // Get the first result from the data subscription synchronously so it's available to
- // the component as soon as possible (and doesn't require a second change detection).
- if (index === 0) {
- return of(data);
- }
- // Promise.resolve is used to avoid synchronously writing the wrong data when
- // two of the Observables in the `combineLatest` stream emit one after
- // another.
- return Promise.resolve(data);
- }))
- .subscribe((data) => {
- // Outlet may have been deactivated or changed names to be associated with a different
- // route
- if (!outlet.isActivated ||
- !outlet.activatedComponentRef ||
- outlet.activatedRoute !== activatedRoute ||
- activatedRoute.component === null) {
- this.unsubscribeFromRouteData(outlet);
- return;
- }
- const mirror = reflectComponentType(activatedRoute.component);
- if (!mirror) {
- this.unsubscribeFromRouteData(outlet);
- return;
- }
- for (const { templateName } of mirror.inputs) {
- outlet.activatedComponentRef.setInput(templateName, data[templateName]);
- }
- });
- this.outletDataSubscriptions.set(outlet, dataSubscription);
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RoutedComponentInputBinder, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
- /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RoutedComponentInputBinder });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RoutedComponentInputBinder, decorators: [{
- type: Injectable
- }] });
- const provideComponentInputBinding = () => {
- return {
- provide: INPUT_BINDER,
- useFactory: componentInputBindingFactory,
- deps: [Router],
- };
- };
- function componentInputBindingFactory(router) {
- /**
- * We cast the router to any here, since the componentInputBindingEnabled
- * property is not available until Angular v16.
- */
- if (router?.componentInputBindingEnabled) {
- return new RoutedComponentInputBinder();
- }
- return null;
- }
- const BACK_BUTTON_INPUTS = ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'routerAnimation', 'text', 'type'];
- let IonBackButton = class IonBackButton {
- routerOutlet;
- navCtrl;
- config;
- r;
- z;
- el;
- constructor(routerOutlet, navCtrl, config, r, z, c) {
- this.routerOutlet = routerOutlet;
- this.navCtrl = navCtrl;
- this.config = config;
- this.r = r;
- this.z = z;
- c.detach();
- this.el = this.r.nativeElement;
- }
- /**
- * @internal
- */
- onClick(ev) {
- const defaultHref = this.defaultHref || this.config.get('backButtonDefaultHref');
- if (this.routerOutlet?.canGoBack()) {
- this.navCtrl.setDirection('back', undefined, undefined, this.routerAnimation);
- this.routerOutlet.pop();
- ev.preventDefault();
- }
- else if (defaultHref != null) {
- this.navCtrl.navigateBack(defaultHref, { animation: this.routerAnimation });
- ev.preventDefault();
- }
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonBackButton, deps: [{ token: IonRouterOutlet, optional: true }, { token: NavController }, { token: Config }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: IonBackButton, inputs: { color: "color", defaultHref: "defaultHref", disabled: "disabled", icon: "icon", mode: "mode", routerAnimation: "routerAnimation", text: "text", type: "type" }, host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 });
- };
- IonBackButton = __decorate([
- ProxyCmp({
- inputs: BACK_BUTTON_INPUTS,
- })
- ], IonBackButton);
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonBackButton, decorators: [{
- type: Directive,
- args: [{
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: BACK_BUTTON_INPUTS,
- }]
- }], ctorParameters: function () { return [{ type: IonRouterOutlet, decorators: [{
- type: Optional
- }] }, { type: NavController }, { type: Config }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { onClick: [{
- type: HostListener,
- args: ['click', ['$event']]
- }] } });
- /**
- * Adds support for Ionic routing directions and animations to the base Angular router link directive.
- *
- * When the router link is clicked, the directive will assign the direction and
- * animation so that the routing integration will transition correctly.
- */
- class RouterLinkDelegateDirective {
- locationStrategy;
- navCtrl;
- elementRef;
- router;
- routerLink;
- routerDirection = 'forward';
- routerAnimation;
- constructor(locationStrategy, navCtrl, elementRef, router, routerLink) {
- this.locationStrategy = locationStrategy;
- this.navCtrl = navCtrl;
- this.elementRef = elementRef;
- this.router = router;
- this.routerLink = routerLink;
- }
- ngOnInit() {
- this.updateTargetUrlAndHref();
- this.updateTabindex();
- }
- ngOnChanges() {
- this.updateTargetUrlAndHref();
- }
- /**
- * The `tabindex` is set to `0` by default on the host element when
- * the `routerLink` directive is used. This causes issues with Ionic
- * components that wrap an `a` or `button` element, such as `ion-item`.
- * See issue https://github.com/angular/angular/issues/28345
- *
- * This method removes the `tabindex` attribute from the host element
- * to allow the Ionic component to manage the focus state correctly.
- */
- updateTabindex() {
- // Ionic components that render a native anchor or button element
- const ionicComponents = [
- 'ION-BACK-BUTTON',
- 'ION-BREADCRUMB',
- 'ION-BUTTON',
- 'ION-CARD',
- 'ION-FAB-BUTTON',
- 'ION-ITEM',
- 'ION-ITEM-OPTION',
- 'ION-MENU-BUTTON',
- 'ION-SEGMENT-BUTTON',
- 'ION-TAB-BUTTON',
- ];
- const hostElement = this.elementRef.nativeElement;
- if (ionicComponents.includes(hostElement.tagName)) {
- if (hostElement.getAttribute('tabindex') === '0') {
- hostElement.removeAttribute('tabindex');
- }
- }
- }
- updateTargetUrlAndHref() {
- if (this.routerLink?.urlTree) {
- const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree));
- this.elementRef.nativeElement.href = href;
- }
- }
- /**
- * @internal
- */
- onClick(ev) {
- this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
- /**
- * This prevents the browser from
- * performing a page reload when pressing
- * an Ionic component with routerLink.
- * The page reload interferes with routing
- * and causes ion-back-button to disappear
- * since the local history is wiped on reload.
- */
- ev.preventDefault();
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RouterLinkDelegateDirective, deps: [{ token: i1.LocationStrategy }, { token: NavController }, { token: i0.ElementRef }, { token: i3.Router }, { token: i3.RouterLink, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RouterLinkDelegateDirective, selector: ":not(a):not(area)[routerLink]", inputs: { routerDirection: "routerDirection", routerAnimation: "routerAnimation" }, host: { listeners: { "click": "onClick($event)" } }, usesOnChanges: true, ngImport: i0 });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RouterLinkDelegateDirective, decorators: [{
- type: Directive,
- args: [{
- selector: ':not(a):not(area)[routerLink]',
- }]
- }], ctorParameters: function () { return [{ type: i1.LocationStrategy }, { type: NavController }, { type: i0.ElementRef }, { type: i3.Router }, { type: i3.RouterLink, decorators: [{
- type: Optional
- }] }]; }, propDecorators: { routerDirection: [{
- type: Input
- }], routerAnimation: [{
- type: Input
- }], onClick: [{
- type: HostListener,
- args: ['click', ['$event']]
- }] } });
- class RouterLinkWithHrefDelegateDirective {
- locationStrategy;
- navCtrl;
- elementRef;
- router;
- routerLink;
- routerDirection = 'forward';
- routerAnimation;
- constructor(locationStrategy, navCtrl, elementRef, router, routerLink) {
- this.locationStrategy = locationStrategy;
- this.navCtrl = navCtrl;
- this.elementRef = elementRef;
- this.router = router;
- this.routerLink = routerLink;
- }
- ngOnInit() {
- this.updateTargetUrlAndHref();
- }
- ngOnChanges() {
- this.updateTargetUrlAndHref();
- }
- updateTargetUrlAndHref() {
- if (this.routerLink?.urlTree) {
- const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree));
- this.elementRef.nativeElement.href = href;
- }
- }
- /**
- * @internal
- */
- onClick() {
- this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RouterLinkWithHrefDelegateDirective, deps: [{ token: i1.LocationStrategy }, { token: NavController }, { token: i0.ElementRef }, { token: i3.Router }, { token: i3.RouterLink, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RouterLinkWithHrefDelegateDirective, selector: "a[routerLink],area[routerLink]", inputs: { routerDirection: "routerDirection", routerAnimation: "routerAnimation" }, host: { listeners: { "click": "onClick()" } }, usesOnChanges: true, ngImport: i0 });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RouterLinkWithHrefDelegateDirective, decorators: [{
- type: Directive,
- args: [{
- selector: 'a[routerLink],area[routerLink]',
- }]
- }], ctorParameters: function () { return [{ type: i1.LocationStrategy }, { type: NavController }, { type: i0.ElementRef }, { type: i3.Router }, { type: i3.RouterLink, decorators: [{
- type: Optional
- }] }]; }, propDecorators: { routerDirection: [{
- type: Input
- }], routerAnimation: [{
- type: Input
- }], onClick: [{
- type: HostListener,
- args: ['click']
- }] } });
- const NAV_INPUTS = ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'];
- const NAV_METHODS = [
- 'push',
- 'insert',
- 'insertPages',
- 'pop',
- 'popTo',
- 'popToRoot',
- 'removeIndex',
- 'setRoot',
- 'setPages',
- 'getActive',
- 'getByIndex',
- 'canGoBack',
- 'getPrevious',
- ];
- let IonNav = class IonNav {
- z;
- el;
- constructor(ref, environmentInjector, injector, angularDelegate, z, c) {
- this.z = z;
- c.detach();
- this.el = ref.nativeElement;
- ref.nativeElement.delegate = angularDelegate.create(environmentInjector, injector);
- proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonNav, deps: [{ token: i0.ElementRef }, { token: i0.EnvironmentInjector }, { token: i0.Injector }, { token: AngularDelegate }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: IonNav, inputs: { animated: "animated", animation: "animation", root: "root", rootParams: "rootParams", swipeGesture: "swipeGesture" }, ngImport: i0 });
- };
- IonNav = __decorate([
- ProxyCmp({
- inputs: NAV_INPUTS,
- methods: NAV_METHODS,
- })
- ], IonNav);
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonNav, decorators: [{
- type: Directive,
- args: [{
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: NAV_INPUTS,
- }]
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.EnvironmentInjector }, { type: i0.Injector }, { type: AngularDelegate }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }]; } });
- // eslint-disable-next-line @angular-eslint/directive-class-suffix
- class IonTabs {
- navCtrl;
- tabsInner;
- /**
- * Emitted before the tab view is changed.
- */
- ionTabsWillChange = new EventEmitter();
- /**
- * Emitted after the tab view is changed.
- */
- ionTabsDidChange = new EventEmitter();
- tabBarSlot = 'bottom';
- hasTab = false;
- selectedTab;
- leavingTab;
- constructor(navCtrl) {
- this.navCtrl = navCtrl;
- }
- ngAfterViewInit() {
- /**
- * Developers must pass at least one ion-tab
- * inside of ion-tabs if they want to use a
- * basic tab-based navigation without the
- * history stack or URL updates associated
- * with the router.
- */
- const firstTab = this.tabs.length > 0 ? this.tabs.first : undefined;
- if (firstTab) {
- this.hasTab = true;
- this.setActiveTab(firstTab.tab);
- this.tabSwitch();
- }
- }
- ngAfterContentInit() {
- this.detectSlotChanges();
- }
- ngAfterContentChecked() {
- this.detectSlotChanges();
- }
- /**
- * @internal
- */
- onStackWillChange({ enteringView, tabSwitch }) {
- const stackId = enteringView.stackId;
- if (tabSwitch && stackId !== undefined) {
- this.ionTabsWillChange.emit({ tab: stackId });
- }
- }
- /**
- * @internal
- */
- onStackDidChange({ enteringView, tabSwitch }) {
- const stackId = enteringView.stackId;
- if (tabSwitch && stackId !== undefined) {
- if (this.tabBar) {
- this.tabBar.selectedTab = stackId;
- }
- this.ionTabsDidChange.emit({ tab: stackId });
- }
- }
- /**
- * When a tab button is clicked, there are several scenarios:
- * 1. If the selected tab is currently active (the tab button has been clicked
- * again), then it should go to the root view for that tab.
- *
- * a. Get the saved root view from the router outlet. If the saved root view
- * matches the tabRootUrl, set the route view to this view including the
- * navigation extras.
- * b. If the saved root view from the router outlet does
- * not match, navigate to the tabRootUrl. No navigation extras are
- * included.
- *
- * 2. If the current tab tab is not currently selected, get the last route
- * view from the router outlet.
- *
- * a. If the last route view exists, navigate to that view including any
- * navigation extras
- * b. If the last route view doesn't exist, then navigate
- * to the default tabRootUrl
- */
- select(tabOrEvent) {
- const isTabString = typeof tabOrEvent === 'string';
- const tab = isTabString ? tabOrEvent : tabOrEvent.detail.tab;
- /**
- * If the tabs are not using the router, then
- * the tab switch logic is handled by the tabs
- * component itself.
- */
- if (this.hasTab) {
- this.setActiveTab(tab);
- this.tabSwitch();
- return;
- }
- const alreadySelected = this.outlet.getActiveStackId() === tab;
- const tabRootUrl = `${this.outlet.tabsPrefix}/${tab}`;
- /**
- * If this is a nested tab, prevent the event
- * from bubbling otherwise the outer tabs
- * will respond to this event too, causing
- * the app to get directed to the wrong place.
- */
- if (!isTabString) {
- tabOrEvent.stopPropagation();
- }
- if (alreadySelected) {
- const activeStackId = this.outlet.getActiveStackId();
- const activeView = this.outlet.getLastRouteView(activeStackId);
- // If on root tab, do not navigate to root tab again
- if (activeView?.url === tabRootUrl) {
- return;
- }
- const rootView = this.outlet.getRootView(tab);
- const navigationExtras = rootView && tabRootUrl === rootView.url && rootView.savedExtras;
- return this.navCtrl.navigateRoot(tabRootUrl, {
- ...navigationExtras,
- animated: true,
- animationDirection: 'back',
- });
- }
- else {
- const lastRoute = this.outlet.getLastRouteView(tab);
- /**
- * If there is a lastRoute, goto that, otherwise goto the fallback url of the
- * selected tab
- */
- const url = lastRoute?.url || tabRootUrl;
- const navigationExtras = lastRoute?.savedExtras;
- return this.navCtrl.navigateRoot(url, {
- ...navigationExtras,
- animated: true,
- animationDirection: 'back',
- });
- }
- }
- setActiveTab(tab) {
- const tabs = this.tabs;
- const selectedTab = tabs.find((t) => t.tab === tab);
- if (!selectedTab) {
- console.error(`[Ionic Error]: Tab with id: "${tab}" does not exist`);
- return;
- }
- this.leavingTab = this.selectedTab;
- this.selectedTab = selectedTab;
- this.ionTabsWillChange.emit({ tab });
- selectedTab.el.active = true;
- }
- tabSwitch() {
- const { selectedTab, leavingTab } = this;
- if (this.tabBar && selectedTab) {
- this.tabBar.selectedTab = selectedTab.tab;
- }
- if (leavingTab?.tab !== selectedTab?.tab) {
- if (leavingTab?.el) {
- leavingTab.el.active = false;
- }
- }
- if (selectedTab) {
- this.ionTabsDidChange.emit({ tab: selectedTab.tab });
- }
- }
- getSelected() {
- if (this.hasTab) {
- return this.selectedTab?.tab;
- }
- return this.outlet.getActiveStackId();
- }
- /**
- * Detects changes to the slot attribute of the tab bar.
- *
- * If the slot attribute has changed, then the tab bar
- * should be relocated to the new slot position.
- */
- detectSlotChanges() {
- this.tabBars.forEach((tabBar) => {
- // el is a protected attribute from the generated component wrapper
- const currentSlot = tabBar.el.getAttribute('slot');
- if (currentSlot !== this.tabBarSlot) {
- this.tabBarSlot = currentSlot;
- this.relocateTabBar();
- }
- });
- }
- /**
- * Relocates the tab bar to the new slot position.
- */
- relocateTabBar() {
- /**
- * `el` is a protected attribute from the generated component wrapper.
- * To avoid having to manually create the wrapper for tab bar, we
- * cast the tab bar to any and access the protected attribute.
- */
- const tabBar = this.tabBar.el;
- if (this.tabBarSlot === 'top') {
- /**
- * A tab bar with a slot of "top" should be inserted
- * at the top of the container.
- */
- this.tabsInner.nativeElement.before(tabBar);
- }
- else {
- /**
- * A tab bar with a slot of "bottom" or without a slot
- * should be inserted at the end of the container.
- */
- this.tabsInner.nativeElement.after(tabBar);
- }
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonTabs, deps: [{ token: NavController }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: IonTabs, selector: "ion-tabs", outputs: { ionTabsWillChange: "ionTabsWillChange", ionTabsDidChange: "ionTabsDidChange" }, host: { listeners: { "ionTabButtonClick": "select($event)" } }, viewQueries: [{ propertyName: "tabsInner", first: true, predicate: ["tabsInner"], descendants: true, read: ElementRef, static: true }], ngImport: i0 });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonTabs, decorators: [{
- type: Directive,
- args: [{
- selector: 'ion-tabs',
- }]
- }], ctorParameters: function () { return [{ type: NavController }]; }, propDecorators: { tabsInner: [{
- type: ViewChild,
- args: ['tabsInner', { read: ElementRef, static: true }]
- }], ionTabsWillChange: [{
- type: Output
- }], ionTabsDidChange: [{
- type: Output
- }], select: [{
- type: HostListener,
- args: ['ionTabButtonClick', ['$event']]
- }] } });
- const raf = (h) => {
- if (typeof __zone_symbol__requestAnimationFrame === 'function') {
- return __zone_symbol__requestAnimationFrame(h);
- }
- if (typeof requestAnimationFrame === 'function') {
- return requestAnimationFrame(h);
- }
- return setTimeout(h);
- };
- // TODO(FW-2827): types
- class ValueAccessor {
- injector;
- elementRef;
- onChange = () => {
- /**/
- };
- onTouched = () => {
- /**/
- };
- lastValue;
- statusChanges;
- constructor(injector, elementRef) {
- this.injector = injector;
- this.elementRef = elementRef;
- }
- writeValue(value) {
- this.elementRef.nativeElement.value = this.lastValue = value;
- setIonicClasses(this.elementRef);
- }
- /**
- * Notifies the ControlValueAccessor of a change in the value of the control.
- *
- * This is called by each of the ValueAccessor directives when we want to update
- * the status and validity of the form control. For example with text components this
- * is called when the ionInput event is fired. For select components this is called
- * when the ionChange event is fired.
- *
- * This also updates the Ionic form status classes on the element.
- *
- * @param el The component element.
- * @param value The new value of the control.
- */
- handleValueChange(el, value) {
- if (el === this.elementRef.nativeElement) {
- if (value !== this.lastValue) {
- this.lastValue = value;
- this.onChange(value);
- }
- setIonicClasses(this.elementRef);
- }
- }
- _handleBlurEvent(el) {
- if (el === this.elementRef.nativeElement) {
- this.onTouched();
- setIonicClasses(this.elementRef);
- // When ion-radio is blurred, el and this.elementRef.nativeElement are
- // different so we need to check if the closest ion-radio-group is the same
- // as this.elementRef.nativeElement and if so, we need to mark the radio group
- // as touched
- }
- else if (el.closest('ion-radio-group') === this.elementRef.nativeElement) {
- this.onTouched();
- }
- }
- registerOnChange(fn) {
- this.onChange = fn;
- }
- registerOnTouched(fn) {
- this.onTouched = fn;
- }
- setDisabledState(isDisabled) {
- this.elementRef.nativeElement.disabled = isDisabled;
- }
- ngOnDestroy() {
- if (this.statusChanges) {
- this.statusChanges.unsubscribe();
- }
- }
- ngAfterViewInit() {
- let ngControl;
- try {
- ngControl = this.injector.get(NgControl);
- }
- catch {
- /* No FormControl or ngModel binding */
- }
- if (!ngControl) {
- return;
- }
- // Listen for changes in validity, disabled, or pending states
- if (ngControl.statusChanges) {
- this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.elementRef));
- }
- /**
- * TODO FW-2787: Remove this in favor of https://github.com/angular/angular/issues/10887
- * whenever it is implemented.
- */
- const formControl = ngControl.control;
- if (formControl) {
- const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
- methodsToPatch.forEach((method) => {
- if (typeof formControl[method] !== 'undefined') {
- const oldFn = formControl[method].bind(formControl);
- formControl[method] = (...params) => {
- oldFn(...params);
- setIonicClasses(this.elementRef);
- };
- }
- });
- }
- }
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ValueAccessor, deps: [{ token: i0.Injector }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
- /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ValueAccessor, host: { listeners: { "ionBlur": "_handleBlurEvent($event.target)" } }, ngImport: i0 });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ValueAccessor, decorators: [{
- type: Directive
- }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ElementRef }]; }, propDecorators: { _handleBlurEvent: [{
- type: HostListener,
- args: ['ionBlur', ['$event.target']]
- }] } });
- const setIonicClasses = (element) => {
- raf(() => {
- const input = element.nativeElement;
- const hasValue = input.value != null && input.value.toString().length > 0;
- const classes = getClasses(input);
- setClasses(input, classes);
- const item = input.closest('ion-item');
- if (item) {
- if (hasValue) {
- setClasses(item, [...classes, 'item-has-value']);
- }
- else {
- setClasses(item, classes);
- }
- }
- });
- };
- const getClasses = (element) => {
- const classList = element.classList;
- const classes = [];
- for (let i = 0; i < classList.length; i++) {
- const item = classList.item(i);
- if (item !== null && startsWith(item, 'ng-')) {
- classes.push(`ion-${item.substring(3)}`);
- }
- }
- return classes;
- };
- const setClasses = (element, classes) => {
- const classList = element.classList;
- classList.remove('ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine');
- classList.add(...classes);
- };
- const startsWith = (input, search) => {
- return input.substring(0, search.length) === search;
- };
- /**
- * Provides a way to customize when activated routes get reused.
- */
- class IonicRouteStrategy {
- /**
- * Whether the given route should detach for later reuse.
- */
- shouldDetach(_route) {
- return false;
- }
- /**
- * Returns `false`, meaning the route (and its subtree) is never reattached
- */
- shouldAttach(_route) {
- return false;
- }
- /**
- * A no-op; the route is never stored since this strategy never detaches routes for later re-use.
- */
- store(_route, _detachedTree) {
- return;
- }
- /**
- * Returns `null` because this strategy does not store routes for later re-use.
- */
- retrieve(_route) {
- return null;
- }
- /**
- * Determines if a route should be reused.
- * This strategy returns `true` when the future route config and
- * current route config are identical and all route parameters are identical.
- */
- shouldReuseRoute(future, curr) {
- if (future.routeConfig !== curr.routeConfig) {
- return false;
- }
- // checking router params
- const futureParams = future.params;
- const currentParams = curr.params;
- const keysA = Object.keys(futureParams);
- const keysB = Object.keys(currentParams);
- if (keysA.length !== keysB.length) {
- return false;
- }
- // Test for A's keys different from B.
- for (const key of keysA) {
- if (currentParams[key] !== futureParams[key]) {
- return false;
- }
- }
- return true;
- }
- }
- // TODO(FW-2827): types
- class OverlayBaseController {
- ctrl;
- constructor(ctrl) {
- this.ctrl = ctrl;
- }
- /**
- * Creates a new overlay
- */
- create(opts) {
- return this.ctrl.create((opts || {}));
- }
- /**
- * When `id` is not provided, it dismisses the top overlay.
- */
- dismiss(data, role, id) {
- return this.ctrl.dismiss(data, role, id);
- }
- /**
- * Returns the top overlay.
- */
- getTop() {
- return this.ctrl.getTop();
- }
- }
- /**
- * Generated bundle index. Do not edit.
- */
- export { AngularDelegate, Config, ConfigToken, DomController, IonBackButton, IonModal, IonNav, IonPopover, IonRouterOutlet, IonTabs, IonicRouteStrategy, MenuController, NavController, NavParams, OverlayBaseController, Platform, ProxyCmp, RouterLinkDelegateDirective, RouterLinkWithHrefDelegateDirective, ValueAccessor, bindLifecycleEvents, provideComponentInputBinding, raf, setIonicClasses };
- //# sourceMappingURL=ionic-angular-common.mjs.map
|