UNPKG

100 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { 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';
3import * as i3 from '@angular/router';
4import { NavigationStart, PRIMARY_OUTLET, ChildrenOutletContexts, ActivatedRoute, Router } from '@angular/router';
5import * as i1 from '@angular/common';
6import { DOCUMENT } from '@angular/common';
7import { isPlatform, getPlatforms, LIFECYCLE_WILL_ENTER, LIFECYCLE_DID_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_UNLOAD, componentOnReady } from '@ionic/core/components';
8import { Subject, fromEvent, BehaviorSubject, combineLatest, of } from 'rxjs';
9import { __decorate } from 'tslib';
10import { filter, switchMap, distinctUntilChanged } from 'rxjs/operators';
11import { NgControl } from '@angular/forms';
12
13class MenuController {
14 constructor(menuController) {
15 this.menuController = menuController;
16 }
17 /**
18 * Programmatically open the Menu.
19 * @param [menuId] Optionally get the menu by its id, or side.
20 * @return returns a promise when the menu is fully opened
21 */
22 open(menuId) {
23 return this.menuController.open(menuId);
24 }
25 /**
26 * Programmatically close the Menu. If no `menuId` is given as the first
27 * argument then it'll close any menu which is open. If a `menuId`
28 * is given then it'll close that exact menu.
29 * @param [menuId] Optionally get the menu by its id, or side.
30 * @return returns a promise when the menu is fully closed
31 */
32 close(menuId) {
33 return this.menuController.close(menuId);
34 }
35 /**
36 * Toggle the menu. If it's closed, it will open, and if opened, it
37 * will close.
38 * @param [menuId] Optionally get the menu by its id, or side.
39 * @return returns a promise when the menu has been toggled
40 */
41 toggle(menuId) {
42 return this.menuController.toggle(menuId);
43 }
44 /**
45 * Used to enable or disable a menu. For example, there could be multiple
46 * left menus, but only one of them should be able to be opened at the same
47 * time. If there are multiple menus on the same side, then enabling one menu
48 * will also automatically disable all the others that are on the same side.
49 * @param [menuId] Optionally get the menu by its id, or side.
50 * @return Returns the instance of the menu, which is useful for chaining.
51 */
52 enable(shouldEnable, menuId) {
53 return this.menuController.enable(shouldEnable, menuId);
54 }
55 /**
56 * Used to enable or disable the ability to swipe open the menu.
57 * @param shouldEnable True if it should be swipe-able, false if not.
58 * @param [menuId] Optionally get the menu by its id, or side.
59 * @return Returns the instance of the menu, which is useful for chaining.
60 */
61 swipeGesture(shouldEnable, menuId) {
62 return this.menuController.swipeGesture(shouldEnable, menuId);
63 }
64 /**
65 * @param [menuId] Optionally get the menu by its id, or side.
66 * @return Returns true if the specified menu is currently open, otherwise false.
67 * If the menuId is not specified, it returns true if ANY menu is currenly open.
68 */
69 isOpen(menuId) {
70 return this.menuController.isOpen(menuId);
71 }
72 /**
73 * @param [menuId] Optionally get the menu by its id, or side.
74 * @return Returns true if the menu is currently enabled, otherwise false.
75 */
76 isEnabled(menuId) {
77 return this.menuController.isEnabled(menuId);
78 }
79 /**
80 * Used to get a menu instance. If a `menuId` is not provided then it'll
81 * return the first menu found. If a `menuId` is `left` or `right`, then
82 * it'll return the enabled menu on that side. Otherwise, if a `menuId` is
83 * provided, then it'll try to find the menu using the menu's `id`
84 * property. If a menu is not found then it'll return `null`.
85 * @param [menuId] Optionally get the menu by its id, or side.
86 * @return Returns the instance of the menu if found, otherwise `null`.
87 */
88 get(menuId) {
89 return this.menuController.get(menuId);
90 }
91 /**
92 * @return Returns the instance of the menu already opened, otherwise `null`.
93 */
94 getOpen() {
95 return this.menuController.getOpen();
96 }
97 /**
98 * @return Returns an array of all menu instances.
99 */
100 getMenus() {
101 return this.menuController.getMenus();
102 }
103 registerAnimation(name, animation) {
104 return this.menuController.registerAnimation(name, animation);
105 }
106 isAnimating() {
107 return this.menuController.isAnimating();
108 }
109 _getOpenSync() {
110 return this.menuController._getOpenSync();
111 }
112 _createAnimation(type, menuCmp) {
113 return this.menuController._createAnimation(type, menuCmp);
114 }
115 _register(menu) {
116 return this.menuController._register(menu);
117 }
118 _unregister(menu) {
119 return this.menuController._unregister(menu);
120 }
121 _setOpen(menu, shouldOpen, animated) {
122 return this.menuController._setOpen(menu, shouldOpen, animated);
123 }
124}
125
126class DomController {
127 /**
128 * Schedules a task to run during the READ phase of the next frame.
129 * This task should only read the DOM, but never modify it.
130 */
131 read(cb) {
132 getQueue().read(cb);
133 }
134 /**
135 * Schedules a task to run during the WRITE phase of the next frame.
136 * This task should write the DOM, but never READ it.
137 */
138 write(cb) {
139 getQueue().write(cb);
140 }
141}
142/** @nocollapse */ DomController.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DomController, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
143/** @nocollapse */ DomController.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DomController, providedIn: 'root' });
144i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DomController, decorators: [{
145 type: Injectable,
146 args: [{
147 providedIn: 'root',
148 }]
149 }] });
150const getQueue = () => {
151 const win = typeof window !== 'undefined' ? window : null;
152 if (win != null) {
153 const Ionic = win.Ionic;
154 if (Ionic?.queue) {
155 return Ionic.queue;
156 }
157 return {
158 read: (cb) => win.requestAnimationFrame(cb),
159 write: (cb) => win.requestAnimationFrame(cb),
160 };
161 }
162 return {
163 read: (cb) => cb(),
164 write: (cb) => cb(),
165 };
166};
167
168class Platform {
169 constructor(doc, zone) {
170 this.doc = doc;
171 /**
172 * @hidden
173 */
174 this.backButton = new Subject();
175 /**
176 * The keyboardDidShow event emits when the
177 * on-screen keyboard is presented.
178 */
179 this.keyboardDidShow = new Subject();
180 /**
181 * The keyboardDidHide event emits when the
182 * on-screen keyboard is hidden.
183 */
184 this.keyboardDidHide = new Subject();
185 /**
186 * The pause event emits when the native platform puts the application
187 * into the background, typically when the user switches to a different
188 * application. This event would emit when a Cordova app is put into
189 * the background, however, it would not fire on a standard web browser.
190 */
191 this.pause = new Subject();
192 /**
193 * The resume event emits when the native platform pulls the application
194 * out from the background. This event would emit when a Cordova app comes
195 * out from the background, however, it would not fire on a standard web browser.
196 */
197 this.resume = new Subject();
198 /**
199 * The resize event emits when the browser window has changed dimensions. This
200 * could be from a browser window being physically resized, or from a device
201 * changing orientation.
202 */
203 this.resize = new Subject();
204 zone.run(() => {
205 this.win = doc.defaultView;
206 this.backButton.subscribeWithPriority = function (priority, callback) {
207 return this.subscribe((ev) => {
208 return ev.register(priority, (processNextHandler) => zone.run(() => callback(processNextHandler)));
209 });
210 };
211 proxyEvent(this.pause, doc, 'pause', zone);
212 proxyEvent(this.resume, doc, 'resume', zone);
213 proxyEvent(this.backButton, doc, 'ionBackButton', zone);
214 proxyEvent(this.resize, this.win, 'resize', zone);
215 proxyEvent(this.keyboardDidShow, this.win, 'ionKeyboardDidShow', zone);
216 proxyEvent(this.keyboardDidHide, this.win, 'ionKeyboardDidHide', zone);
217 let readyResolve;
218 this._readyPromise = new Promise((res) => {
219 readyResolve = res;
220 });
221 if (this.win?.['cordova']) {
222 doc.addEventListener('deviceready', () => {
223 readyResolve('cordova');
224 }, { once: true });
225 }
226 else {
227 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
228 readyResolve('dom');
229 }
230 });
231 }
232 /**
233 * @returns returns true/false based on platform.
234 * @description
235 * Depending on the platform the user is on, `is(platformName)` will
236 * return `true` or `false`. Note that the same app can return `true`
237 * for more than one platform name. For example, an app running from
238 * an iPad would return `true` for the platform names: `mobile`,
239 * `ios`, `ipad`, and `tablet`. Additionally, if the app was running
240 * from Cordova then `cordova` would be true, and if it was running
241 * from a web browser on the iPad then `mobileweb` would be `true`.
242 *
243 * ```
244 * import { Platform } from 'ionic-angular';
245 *
246 * @Component({...})
247 * export MyPage {
248 * constructor(public platform: Platform) {
249 * if (this.platform.is('ios')) {
250 * // This will only print when on iOS
251 * console.log('I am an iOS device!');
252 * }
253 * }
254 * }
255 * ```
256 *
257 * | Platform Name | Description |
258 * |-----------------|------------------------------------|
259 * | android | on a device running Android. |
260 * | capacitor | on a device running Capacitor. |
261 * | cordova | on a device running Cordova. |
262 * | ios | on a device running iOS. |
263 * | ipad | on an iPad device. |
264 * | iphone | on an iPhone device. |
265 * | phablet | on a phablet device. |
266 * | tablet | on a tablet device. |
267 * | electron | in Electron on a desktop device. |
268 * | pwa | as a PWA app. |
269 * | mobile | on a mobile device. |
270 * | mobileweb | on a mobile device in a browser. |
271 * | desktop | on a desktop device. |
272 * | hybrid | is a cordova or capacitor app. |
273 *
274 */
275 is(platformName) {
276 return isPlatform(this.win, platformName);
277 }
278 /**
279 * @returns the array of platforms
280 * @description
281 * Depending on what device you are on, `platforms` can return multiple values.
282 * Each possible value is a hierarchy of platforms. For example, on an iPhone,
283 * it would return `mobile`, `ios`, and `iphone`.
284 *
285 * ```
286 * import { Platform } from 'ionic-angular';
287 *
288 * @Component({...})
289 * export MyPage {
290 * constructor(public platform: Platform) {
291 * // This will print an array of the current platforms
292 * console.log(this.platform.platforms());
293 * }
294 * }
295 * ```
296 */
297 platforms() {
298 return getPlatforms(this.win);
299 }
300 /**
301 * Returns a promise when the platform is ready and native functionality
302 * can be called. If the app is running from within a web browser, then
303 * the promise will resolve when the DOM is ready. When the app is running
304 * from an application engine such as Cordova, then the promise will
305 * resolve when Cordova triggers the `deviceready` event.
306 *
307 * The resolved value is the `readySource`, which states which platform
308 * ready was used. For example, when Cordova is ready, the resolved ready
309 * source is `cordova`. The default ready source value will be `dom`. The
310 * `readySource` is useful if different logic should run depending on the
311 * platform the app is running from. For example, only Cordova can execute
312 * the status bar plugin, so the web should not run status bar plugin logic.
313 *
314 * ```
315 * import { Component } from '@angular/core';
316 * import { Platform } from 'ionic-angular';
317 *
318 * @Component({...})
319 * export MyApp {
320 * constructor(public platform: Platform) {
321 * this.platform.ready().then((readySource) => {
322 * console.log('Platform ready from', readySource);
323 * // Platform now ready, execute any required native code
324 * });
325 * }
326 * }
327 * ```
328 */
329 ready() {
330 return this._readyPromise;
331 }
332 /**
333 * Returns if this app is using right-to-left language direction or not.
334 * We recommend the app's `index.html` file already has the correct `dir`
335 * attribute value set, such as `<html dir="ltr">` or `<html dir="rtl">`.
336 * [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
337 */
338 get isRTL() {
339 return this.doc.dir === 'rtl';
340 }
341 /**
342 * Get the query string parameter
343 */
344 getQueryParam(key) {
345 return readQueryParam(this.win.location.href, key);
346 }
347 /**
348 * Returns `true` if the app is in landscape mode.
349 */
350 isLandscape() {
351 return !this.isPortrait();
352 }
353 /**
354 * Returns `true` if the app is in portrait mode.
355 */
356 isPortrait() {
357 return this.win.matchMedia?.('(orientation: portrait)').matches;
358 }
359 testUserAgent(expression) {
360 const nav = this.win.navigator;
361 return !!(nav?.userAgent && nav.userAgent.indexOf(expression) >= 0);
362 }
363 /**
364 * Get the current url.
365 */
366 url() {
367 return this.win.location.href;
368 }
369 /**
370 * Gets the width of the platform's viewport using `window.innerWidth`.
371 */
372 width() {
373 return this.win.innerWidth;
374 }
375 /**
376 * Gets the height of the platform's viewport using `window.innerHeight`.
377 */
378 height() {
379 return this.win.innerHeight;
380 }
381}
382/** @nocollapse */ Platform.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: Platform, deps: [{ token: DOCUMENT }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
383/** @nocollapse */ Platform.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: Platform, providedIn: 'root' });
384i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: Platform, decorators: [{
385 type: Injectable,
386 args: [{
387 providedIn: 'root',
388 }]
389 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
390 type: Inject,
391 args: [DOCUMENT]
392 }] }, { type: i0.NgZone }]; } });
393const readQueryParam = (url, key) => {
394 key = key.replace(/[[\]\\]/g, '\\$&');
395 const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
396 const results = regex.exec(url);
397 return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
398};
399const proxyEvent = (emitter, el, eventName, zone) => {
400 if (el) {
401 el.addEventListener(eventName, (ev) => {
402 /**
403 * `zone.run` is required to make sure that we are running inside the Angular zone
404 * at all times. This is necessary since an app that has Capacitor will
405 * override the `document.addEventListener` with its own implementation.
406 * The override causes the event to no longer be in the Angular zone.
407 */
408 zone.run(() => {
409 // ?? cordova might emit "null" events
410 const value = ev != null ? ev.detail : undefined;
411 emitter.next(value);
412 });
413 });
414 }
415};
416
417class NavController {
418 constructor(platform, location, serializer, router) {
419 this.location = location;
420 this.serializer = serializer;
421 this.router = router;
422 this.direction = DEFAULT_DIRECTION;
423 this.animated = DEFAULT_ANIMATED;
424 this.guessDirection = 'forward';
425 this.lastNavId = -1;
426 // Subscribe to router events to detect direction
427 if (router) {
428 router.events.subscribe((ev) => {
429 if (ev instanceof NavigationStart) {
430 const id = ev.restoredState ? ev.restoredState.navigationId : ev.id;
431 this.guessDirection = id < this.lastNavId ? 'back' : 'forward';
432 this.guessAnimation = !ev.restoredState ? this.guessDirection : undefined;
433 this.lastNavId = this.guessDirection === 'forward' ? ev.id : id;
434 }
435 });
436 }
437 // Subscribe to backButton events
438 platform.backButton.subscribeWithPriority(0, (processNextHandler) => {
439 this.pop();
440 processNextHandler();
441 });
442 }
443 /**
444 * This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
445 * it's equivalent to calling `this.router.navigateByUrl()`, but it's explicit about the **direction** of the transition.
446 *
447 * Going **forward** means that a new page is going to be pushed to the stack of the outlet (ion-router-outlet),
448 * and that it will show a "forward" animation by default.
449 *
450 * Navigating forward can also be triggered in a declarative manner by using the `[routerDirection]` directive:
451 *
452 * ```html
453 * <a routerLink="/path/to/page" routerDirection="forward">Link</a>
454 * ```
455 */
456 navigateForward(url, options = {}) {
457 this.setDirection('forward', options.animated, options.animationDirection, options.animation);
458 return this.navigate(url, options);
459 }
460 /**
461 * This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
462 * it's equivalent to calling:
463 *
464 * ```ts
465 * this.navController.setDirection('back');
466 * this.router.navigateByUrl(path);
467 * ```
468 *
469 * Going **back** means that all the pages in the stack until the navigated page is found will be popped,
470 * and that it will show a "back" animation by default.
471 *
472 * Navigating back can also be triggered in a declarative manner by using the `[routerDirection]` directive:
473 *
474 * ```html
475 * <a routerLink="/path/to/page" routerDirection="back">Link</a>
476 * ```
477 */
478 navigateBack(url, options = {}) {
479 this.setDirection('back', options.animated, options.animationDirection, options.animation);
480 return this.navigate(url, options);
481 }
482 /**
483 * This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
484 * it's equivalent to calling:
485 *
486 * ```ts
487 * this.navController.setDirection('root');
488 * this.router.navigateByUrl(path);
489 * ```
490 *
491 * Going **root** means that all existing pages in the stack will be removed,
492 * and the navigated page will become the single page in the stack.
493 *
494 * Navigating root can also be triggered in a declarative manner by using the `[routerDirection]` directive:
495 *
496 * ```html
497 * <a routerLink="/path/to/page" routerDirection="root">Link</a>
498 * ```
499 */
500 navigateRoot(url, options = {}) {
501 this.setDirection('root', options.animated, options.animationDirection, options.animation);
502 return this.navigate(url, options);
503 }
504 /**
505 * Same as [Location](https://angular.io/api/common/Location)'s back() method.
506 * It will use the standard `window.history.back()` under the hood, but featuring a `back` animation
507 * by default.
508 */
509 back(options = { animated: true, animationDirection: 'back' }) {
510 this.setDirection('back', options.animated, options.animationDirection, options.animation);
511 return this.location.back();
512 }
513 /**
514 * This methods goes back in the context of Ionic's stack navigation.
515 *
516 * It recursively finds the top active `ion-router-outlet` and calls `pop()`.
517 * This is the recommended way to go back when you are using `ion-router-outlet`.
518 *
519 * Resolves to `true` if it was able to pop.
520 */
521 async pop() {
522 let outlet = this.topOutlet;
523 while (outlet) {
524 if (await outlet.pop()) {
525 return true;
526 }
527 else {
528 outlet = outlet.parentOutlet;
529 }
530 }
531 return false;
532 }
533 /**
534 * This methods specifies the direction of the next navigation performed by the Angular router.
535 *
536 * `setDirection()` does not trigger any transition, it just sets some flags to be consumed by `ion-router-outlet`.
537 *
538 * It's recommended to use `navigateForward()`, `navigateBack()` and `navigateRoot()` instead of `setDirection()`.
539 */
540 setDirection(direction, animated, animationDirection, animationBuilder) {
541 this.direction = direction;
542 this.animated = getAnimation(direction, animated, animationDirection);
543 this.animationBuilder = animationBuilder;
544 }
545 /**
546 * @internal
547 */
548 setTopOutlet(outlet) {
549 this.topOutlet = outlet;
550 }
551 /**
552 * @internal
553 */
554 consumeTransition() {
555 let direction = 'root';
556 let animation;
557 const animationBuilder = this.animationBuilder;
558 if (this.direction === 'auto') {
559 direction = this.guessDirection;
560 animation = this.guessAnimation;
561 }
562 else {
563 animation = this.animated;
564 direction = this.direction;
565 }
566 this.direction = DEFAULT_DIRECTION;
567 this.animated = DEFAULT_ANIMATED;
568 this.animationBuilder = undefined;
569 return {
570 direction,
571 animation,
572 animationBuilder,
573 };
574 }
575 navigate(url, options) {
576 if (Array.isArray(url)) {
577 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
578 return this.router.navigate(url, options);
579 }
580 else {
581 /**
582 * navigateByUrl ignores any properties that
583 * would change the url, so things like queryParams
584 * would be ignored unless we create a url tree
585 * More Info: https://github.com/angular/angular/issues/18798
586 */
587 const urlTree = this.serializer.parse(url.toString());
588 if (options.queryParams !== undefined) {
589 urlTree.queryParams = { ...options.queryParams };
590 }
591 if (options.fragment !== undefined) {
592 urlTree.fragment = options.fragment;
593 }
594 /**
595 * `navigateByUrl` will still apply `NavigationExtras` properties
596 * that do not modify the url, such as `replaceUrl` which is why
597 * `options` is passed in here.
598 */
599 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
600 return this.router.navigateByUrl(urlTree, options);
601 }
602 }
603}
604/** @nocollapse */ NavController.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: NavController, deps: [{ token: Platform }, { token: i1.Location }, { token: i3.UrlSerializer }, { token: i3.Router, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
605/** @nocollapse */ NavController.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: NavController, providedIn: 'root' });
606i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: NavController, decorators: [{
607 type: Injectable,
608 args: [{
609 providedIn: 'root',
610 }]
611 }], ctorParameters: function () { return [{ type: Platform }, { type: i1.Location }, { type: i3.UrlSerializer }, { type: i3.Router, decorators: [{
612 type: Optional
613 }] }]; } });
614const getAnimation = (direction, animated, animationDirection) => {
615 if (animated === false) {
616 return undefined;
617 }
618 if (animationDirection !== undefined) {
619 return animationDirection;
620 }
621 if (direction === 'forward' || direction === 'back') {
622 return direction;
623 }
624 else if (direction === 'root' && animated === true) {
625 return 'forward';
626 }
627 return undefined;
628};
629const DEFAULT_DIRECTION = 'auto';
630const DEFAULT_ANIMATED = undefined;
631
632class Config {
633 get(key, fallback) {
634 const c = getConfig();
635 if (c) {
636 return c.get(key, fallback);
637 }
638 return null;
639 }
640 getBoolean(key, fallback) {
641 const c = getConfig();
642 if (c) {
643 return c.getBoolean(key, fallback);
644 }
645 return false;
646 }
647 getNumber(key, fallback) {
648 const c = getConfig();
649 if (c) {
650 return c.getNumber(key, fallback);
651 }
652 return 0;
653 }
654}
655/** @nocollapse */ Config.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: Config, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
656/** @nocollapse */ Config.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: Config, providedIn: 'root' });
657i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: Config, decorators: [{
658 type: Injectable,
659 args: [{
660 providedIn: 'root',
661 }]
662 }] });
663const ConfigToken = new InjectionToken('USERCONFIG');
664const getConfig = () => {
665 if (typeof window !== 'undefined') {
666 const Ionic = window.Ionic;
667 if (Ionic?.config) {
668 return Ionic.config;
669 }
670 }
671 return null;
672};
673
674/**
675 * @description
676 * NavParams are an object that exists on a page and can contain data for that particular view.
677 * Similar to how data was pass to a view in V1 with `$stateParams`, NavParams offer a much more flexible
678 * option with a simple `get` method.
679 *
680 * @usage
681 * ```ts
682 * import { NavParams } from '@ionic/angular';
683 *
684 * export class MyClass{
685 *
686 * constructor(navParams: NavParams){
687 * // userParams is an object we have in our nav-parameters
688 * navParams.get('userParams');
689 * }
690 *
691 * }
692 * ```
693 */
694class NavParams {
695 constructor(data = {}) {
696 this.data = data;
697 }
698 /**
699 * Get the value of a nav-parameter for the current view
700 *
701 * ```ts
702 * import { NavParams } from 'ionic-angular';
703 *
704 * export class MyClass{
705 * constructor(public navParams: NavParams){
706 * // userParams is an object we have in our nav-parameters
707 * this.navParams.get('userParams');
708 * }
709 * }
710 * ```
711 *
712 * @param param Which param you want to look up
713 */
714 get(param) {
715 return this.data[param];
716 }
717}
718
719// TODO(FW-2827): types
720class AngularDelegate {
721 constructor() {
722 this.zone = inject(NgZone);
723 this.applicationRef = inject(ApplicationRef);
724 }
725 create(environmentInjector, injector, elementReferenceKey) {
726 return new AngularFrameworkDelegate(environmentInjector, injector, this.applicationRef, this.zone, elementReferenceKey);
727 }
728}
729/** @nocollapse */ AngularDelegate.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AngularDelegate, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
730/** @nocollapse */ AngularDelegate.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AngularDelegate });
731i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AngularDelegate, decorators: [{
732 type: Injectable
733 }] });
734class AngularFrameworkDelegate {
735 constructor(environmentInjector, injector, applicationRef, zone, elementReferenceKey) {
736 this.environmentInjector = environmentInjector;
737 this.injector = injector;
738 this.applicationRef = applicationRef;
739 this.zone = zone;
740 this.elementReferenceKey = elementReferenceKey;
741 this.elRefMap = new WeakMap();
742 this.elEventsMap = new WeakMap();
743 }
744 attachViewToDom(container, component, params, cssClasses) {
745 return this.zone.run(() => {
746 return new Promise((resolve) => {
747 const componentProps = {
748 ...params,
749 };
750 /**
751 * Ionic Angular passes a reference to a modal
752 * or popover that can be accessed using a
753 * variable in the overlay component. If
754 * elementReferenceKey is defined, then we should
755 * pass a reference to the component using
756 * elementReferenceKey as the key.
757 */
758 if (this.elementReferenceKey !== undefined) {
759 componentProps[this.elementReferenceKey] = container;
760 }
761 const el = attachView(this.zone, this.environmentInjector, this.injector, this.applicationRef, this.elRefMap, this.elEventsMap, container, component, componentProps, cssClasses, this.elementReferenceKey);
762 resolve(el);
763 });
764 });
765 }
766 removeViewFromDom(_container, component) {
767 return this.zone.run(() => {
768 return new Promise((resolve) => {
769 const componentRef = this.elRefMap.get(component);
770 if (componentRef) {
771 componentRef.destroy();
772 this.elRefMap.delete(component);
773 const unbindEvents = this.elEventsMap.get(component);
774 if (unbindEvents) {
775 unbindEvents();
776 this.elEventsMap.delete(component);
777 }
778 }
779 resolve();
780 });
781 });
782 }
783}
784const attachView = (zone, environmentInjector, injector, applicationRef, elRefMap, elEventsMap, container, component, params, cssClasses, elementReferenceKey) => {
785 /**
786 * Wraps the injector with a custom injector that
787 * provides NavParams to the component.
788 *
789 * NavParams is a legacy feature from Ionic v3 that allows
790 * Angular developers to provide data to a component
791 * and access it by providing NavParams as a dependency
792 * in the constructor.
793 *
794 * The modern approach is to access the data directly
795 * from the component's class instance.
796 */
797 const childInjector = Injector.create({
798 providers: getProviders(params),
799 parent: injector,
800 });
801 const componentRef = createComponent(component, {
802 environmentInjector,
803 elementInjector: childInjector,
804 });
805 const instance = componentRef.instance;
806 const hostElement = componentRef.location.nativeElement;
807 if (params) {
808 /**
809 * For modals and popovers, a reference to the component is
810 * added to `params` during the call to attachViewToDom. If
811 * a reference using this name is already set, this means
812 * the app is trying to use the name as a component prop,
813 * which will cause collisions.
814 */
815 if (elementReferenceKey && instance[elementReferenceKey] !== undefined) {
816 console.error(`[Ionic Error]: ${elementReferenceKey} is a reserved property when using ${container.tagName.toLowerCase()}. Rename or remove the "${elementReferenceKey}" property from ${component.name}.`);
817 }
818 Object.assign(instance, params);
819 }
820 if (cssClasses) {
821 for (const cssClass of cssClasses) {
822 hostElement.classList.add(cssClass);
823 }
824 }
825 const unbindEvents = bindLifecycleEvents(zone, instance, hostElement);
826 container.appendChild(hostElement);
827 applicationRef.attachView(componentRef.hostView);
828 elRefMap.set(hostElement, componentRef);
829 elEventsMap.set(hostElement, unbindEvents);
830 return hostElement;
831};
832const LIFECYCLES = [
833 LIFECYCLE_WILL_ENTER,
834 LIFECYCLE_DID_ENTER,
835 LIFECYCLE_WILL_LEAVE,
836 LIFECYCLE_DID_LEAVE,
837 LIFECYCLE_WILL_UNLOAD,
838];
839const bindLifecycleEvents = (zone, instance, element) => {
840 return zone.run(() => {
841 const unregisters = LIFECYCLES.filter((eventName) => typeof instance[eventName] === 'function').map((eventName) => {
842 const handler = (ev) => instance[eventName](ev.detail);
843 element.addEventListener(eventName, handler);
844 return () => element.removeEventListener(eventName, handler);
845 });
846 return () => unregisters.forEach((fn) => fn());
847 });
848};
849const NavParamsToken = new InjectionToken('NavParamsToken');
850const getProviders = (params) => {
851 return [
852 {
853 provide: NavParamsToken,
854 useValue: params,
855 },
856 {
857 provide: NavParams,
858 useFactory: provideNavParamsInjectable,
859 deps: [NavParamsToken],
860 },
861 ];
862};
863const provideNavParamsInjectable = (params) => {
864 return new NavParams(params);
865};
866
867// TODO: Is there a way we can grab this from angular-component-lib instead?
868const proxyInputs = (Cmp, inputs) => {
869 const Prototype = Cmp.prototype;
870 inputs.forEach((item) => {
871 Object.defineProperty(Prototype, item, {
872 get() {
873 return this.el[item];
874 },
875 set(val) {
876 this.z.runOutsideAngular(() => (this.el[item] = val));
877 },
878 });
879 });
880};
881const proxyMethods = (Cmp, methods) => {
882 const Prototype = Cmp.prototype;
883 methods.forEach((methodName) => {
884 Prototype[methodName] = function () {
885 const args = arguments;
886 return this.z.runOutsideAngular(() => this.el[methodName].apply(this.el, args));
887 };
888 });
889};
890const proxyOutputs = (instance, el, events) => {
891 events.forEach((eventName) => (instance[eventName] = fromEvent(el, eventName)));
892};
893// tslint:disable-next-line: only-arrow-functions
894function ProxyCmp(opts) {
895 const decorator = function (cls) {
896 const { defineCustomElementFn, inputs, methods } = opts;
897 if (defineCustomElementFn !== undefined) {
898 defineCustomElementFn();
899 }
900 if (inputs) {
901 proxyInputs(cls, inputs);
902 }
903 if (methods) {
904 proxyMethods(cls, methods);
905 }
906 return cls;
907 };
908 return decorator;
909}
910
911const POPOVER_INPUTS = [
912 'alignment',
913 'animated',
914 'arrow',
915 'keepContentsMounted',
916 'backdropDismiss',
917 'cssClass',
918 'dismissOnSelect',
919 'enterAnimation',
920 'event',
921 'isOpen',
922 'keyboardClose',
923 'leaveAnimation',
924 'mode',
925 'showBackdrop',
926 'translucent',
927 'trigger',
928 'triggerAction',
929 'reference',
930 'size',
931 'side',
932];
933const POPOVER_METHODS = ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'];
934let IonPopover = class IonPopover {
935 constructor(c, r, z) {
936 this.z = z;
937 this.isCmpOpen = false;
938 this.el = r.nativeElement;
939 this.el.addEventListener('ionMount', () => {
940 this.isCmpOpen = true;
941 c.detectChanges();
942 });
943 this.el.addEventListener('didDismiss', () => {
944 this.isCmpOpen = false;
945 c.detectChanges();
946 });
947 proxyOutputs(this, this.el, [
948 'ionPopoverDidPresent',
949 'ionPopoverWillPresent',
950 'ionPopoverWillDismiss',
951 'ionPopoverDidDismiss',
952 'didPresent',
953 'willPresent',
954 'willDismiss',
955 'didDismiss',
956 ]);
957 }
958};
959/** @nocollapse */ IonPopover.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonPopover, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
960/** @nocollapse */ IonPopover.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.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", 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 });
961IonPopover = __decorate([
962 ProxyCmp({
963 inputs: POPOVER_INPUTS,
964 methods: POPOVER_METHODS,
965 })
966 /**
967 * @Component extends from @Directive
968 * so by defining the inputs here we
969 * do not need to re-define them for the
970 * lazy loaded popover.
971 */
972], IonPopover);
973i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonPopover, decorators: [{
974 type: Directive,
975 args: [{
976 selector: 'ion-popover',
977 // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
978 inputs: POPOVER_INPUTS,
979 }]
980 }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { template: [{
981 type: ContentChild,
982 args: [TemplateRef, { static: false }]
983 }] } });
984
985const MODAL_INPUTS = [
986 'animated',
987 'keepContentsMounted',
988 'backdropBreakpoint',
989 'backdropDismiss',
990 'breakpoints',
991 'canDismiss',
992 'cssClass',
993 'enterAnimation',
994 'event',
995 'handle',
996 'handleBehavior',
997 'initialBreakpoint',
998 'isOpen',
999 'keyboardClose',
1000 'leaveAnimation',
1001 'mode',
1002 'presentingElement',
1003 'showBackdrop',
1004 'translucent',
1005 'trigger',
1006];
1007const MODAL_METHODS = [
1008 'present',
1009 'dismiss',
1010 'onDidDismiss',
1011 'onWillDismiss',
1012 'setCurrentBreakpoint',
1013 'getCurrentBreakpoint',
1014];
1015let IonModal = class IonModal {
1016 constructor(c, r, z) {
1017 this.z = z;
1018 this.isCmpOpen = false;
1019 this.el = r.nativeElement;
1020 this.el.addEventListener('ionMount', () => {
1021 this.isCmpOpen = true;
1022 c.detectChanges();
1023 });
1024 this.el.addEventListener('didDismiss', () => {
1025 this.isCmpOpen = false;
1026 c.detectChanges();
1027 });
1028 proxyOutputs(this, this.el, [
1029 'ionModalDidPresent',
1030 'ionModalWillPresent',
1031 'ionModalWillDismiss',
1032 'ionModalDidDismiss',
1033 'ionBreakpointDidChange',
1034 'didPresent',
1035 'willPresent',
1036 'willDismiss',
1037 'didDismiss',
1038 ]);
1039 }
1040};
1041/** @nocollapse */ IonModal.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonModal, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
1042/** @nocollapse */ IonModal.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: IonModal, selector: "ion-modal", inputs: { animated: "animated", keepContentsMounted: "keepContentsMounted", backdropBreakpoint: "backdropBreakpoint", backdropDismiss: "backdropDismiss", breakpoints: "breakpoints", canDismiss: "canDismiss", cssClass: "cssClass", enterAnimation: "enterAnimation", event: "event", 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 });
1043IonModal = __decorate([
1044 ProxyCmp({
1045 inputs: MODAL_INPUTS,
1046 methods: MODAL_METHODS,
1047 })
1048 /**
1049 * @Component extends from @Directive
1050 * so by defining the inputs here we
1051 * do not need to re-define them for the
1052 * lazy loaded popover.
1053 */
1054], IonModal);
1055i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonModal, decorators: [{
1056 type: Directive,
1057 args: [{
1058 selector: 'ion-modal',
1059 // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
1060 inputs: MODAL_INPUTS,
1061 }]
1062 }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { template: [{
1063 type: ContentChild,
1064 args: [TemplateRef, { static: false }]
1065 }] } });
1066
1067const insertView = (views, view, direction) => {
1068 if (direction === 'root') {
1069 return setRoot(views, view);
1070 }
1071 else if (direction === 'forward') {
1072 return setForward(views, view);
1073 }
1074 else {
1075 return setBack(views, view);
1076 }
1077};
1078const setRoot = (views, view) => {
1079 views = views.filter((v) => v.stackId !== view.stackId);
1080 views.push(view);
1081 return views;
1082};
1083const setForward = (views, view) => {
1084 const index = views.indexOf(view);
1085 if (index >= 0) {
1086 views = views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
1087 }
1088 else {
1089 views.push(view);
1090 }
1091 return views;
1092};
1093const setBack = (views, view) => {
1094 const index = views.indexOf(view);
1095 if (index >= 0) {
1096 return views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
1097 }
1098 else {
1099 return setRoot(views, view);
1100 }
1101};
1102const getUrl = (router, activatedRoute) => {
1103 const urlTree = router.createUrlTree(['.'], { relativeTo: activatedRoute });
1104 return router.serializeUrl(urlTree);
1105};
1106const isTabSwitch = (enteringView, leavingView) => {
1107 if (!leavingView) {
1108 return true;
1109 }
1110 return enteringView.stackId !== leavingView.stackId;
1111};
1112const computeStackId = (prefixUrl, url) => {
1113 if (!prefixUrl) {
1114 return undefined;
1115 }
1116 const segments = toSegments(url);
1117 for (let i = 0; i < segments.length; i++) {
1118 if (i >= prefixUrl.length) {
1119 return segments[i];
1120 }
1121 if (segments[i] !== prefixUrl[i]) {
1122 return undefined;
1123 }
1124 }
1125 return undefined;
1126};
1127const toSegments = (path) => {
1128 return path
1129 .split('/')
1130 .map((s) => s.trim())
1131 .filter((s) => s !== '');
1132};
1133const destroyView = (view) => {
1134 if (view) {
1135 view.ref.destroy();
1136 view.unlistenEvents();
1137 }
1138};
1139
1140// TODO(FW-2827): types
1141class StackController {
1142 constructor(tabsPrefix, containerEl, router, navCtrl, zone, location) {
1143 this.containerEl = containerEl;
1144 this.router = router;
1145 this.navCtrl = navCtrl;
1146 this.zone = zone;
1147 this.location = location;
1148 this.views = [];
1149 this.skipTransition = false;
1150 this.nextId = 0;
1151 this.tabsPrefix = tabsPrefix !== undefined ? toSegments(tabsPrefix) : undefined;
1152 }
1153 createView(ref, activatedRoute) {
1154 const url = getUrl(this.router, activatedRoute);
1155 const element = ref?.location?.nativeElement;
1156 const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
1157 return {
1158 id: this.nextId++,
1159 stackId: computeStackId(this.tabsPrefix, url),
1160 unlistenEvents,
1161 element,
1162 ref,
1163 url,
1164 };
1165 }
1166 getExistingView(activatedRoute) {
1167 const activatedUrlKey = getUrl(this.router, activatedRoute);
1168 const view = this.views.find((vw) => vw.url === activatedUrlKey);
1169 if (view) {
1170 view.ref.changeDetectorRef.reattach();
1171 }
1172 return view;
1173 }
1174 setActive(enteringView) {
1175 const consumeResult = this.navCtrl.consumeTransition();
1176 let { direction, animation, animationBuilder } = consumeResult;
1177 const leavingView = this.activeView;
1178 const tabSwitch = isTabSwitch(enteringView, leavingView);
1179 if (tabSwitch) {
1180 direction = 'back';
1181 animation = undefined;
1182 }
1183 const viewsSnapshot = this.views.slice();
1184 let currentNavigation;
1185 const router = this.router;
1186 // Angular >= 7.2.0
1187 if (router.getCurrentNavigation) {
1188 currentNavigation = router.getCurrentNavigation();
1189 // Angular < 7.2.0
1190 }
1191 else if (router.navigations?.value) {
1192 currentNavigation = router.navigations.value;
1193 }
1194 /**
1195 * If the navigation action
1196 * sets `replaceUrl: true`
1197 * then we need to make sure
1198 * we remove the last item
1199 * from our views stack
1200 */
1201 if (currentNavigation?.extras?.replaceUrl) {
1202 if (this.views.length > 0) {
1203 this.views.splice(-1, 1);
1204 }
1205 }
1206 const reused = this.views.includes(enteringView);
1207 const views = this.insertView(enteringView, direction);
1208 // Trigger change detection before transition starts
1209 // This will call ngOnInit() the first time too, just after the view
1210 // was attached to the dom, but BEFORE the transition starts
1211 if (!reused) {
1212 enteringView.ref.changeDetectorRef.detectChanges();
1213 }
1214 /**
1215 * If we are going back from a page that
1216 * was presented using a custom animation
1217 * we should default to using that
1218 * unless the developer explicitly
1219 * provided another animation.
1220 */
1221 const customAnimation = enteringView.animationBuilder;
1222 if (animationBuilder === undefined && direction === 'back' && !tabSwitch && customAnimation !== undefined) {
1223 animationBuilder = customAnimation;
1224 }
1225 /**
1226 * Save any custom animation so that navigating
1227 * back will use this custom animation by default.
1228 */
1229 if (leavingView) {
1230 leavingView.animationBuilder = animationBuilder;
1231 }
1232 // Wait until previous transitions finish
1233 return this.zone.runOutsideAngular(() => {
1234 return this.wait(() => {
1235 // disconnect leaving page from change detection to
1236 // reduce jank during the page transition
1237 if (leavingView) {
1238 leavingView.ref.changeDetectorRef.detach();
1239 }
1240 // In case the enteringView is the same as the leavingPage we need to reattach()
1241 enteringView.ref.changeDetectorRef.reattach();
1242 return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false, animationBuilder)
1243 .then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location, this.zone))
1244 .then(() => ({
1245 enteringView,
1246 direction,
1247 animation,
1248 tabSwitch,
1249 }));
1250 });
1251 });
1252 }
1253 canGoBack(deep, stackId = this.getActiveStackId()) {
1254 return this.getStack(stackId).length > deep;
1255 }
1256 pop(deep, stackId = this.getActiveStackId()) {
1257 return this.zone.run(() => {
1258 const views = this.getStack(stackId);
1259 if (views.length <= deep) {
1260 return Promise.resolve(false);
1261 }
1262 const view = views[views.length - deep - 1];
1263 let url = view.url;
1264 const viewSavedData = view.savedData;
1265 if (viewSavedData) {
1266 const primaryOutlet = viewSavedData.get('primary');
1267 if (primaryOutlet?.route?._routerState?.snapshot.url) {
1268 url = primaryOutlet.route._routerState.snapshot.url;
1269 }
1270 }
1271 const { animationBuilder } = this.navCtrl.consumeTransition();
1272 return this.navCtrl.navigateBack(url, { ...view.savedExtras, animation: animationBuilder }).then(() => true);
1273 });
1274 }
1275 startBackTransition() {
1276 const leavingView = this.activeView;
1277 if (leavingView) {
1278 const views = this.getStack(leavingView.stackId);
1279 const enteringView = views[views.length - 2];
1280 const customAnimation = enteringView.animationBuilder;
1281 return this.wait(() => {
1282 return this.transition(enteringView, // entering view
1283 leavingView, // leaving view
1284 'back', this.canGoBack(2), true, customAnimation);
1285 });
1286 }
1287 return Promise.resolve();
1288 }
1289 endBackTransition(shouldComplete) {
1290 if (shouldComplete) {
1291 this.skipTransition = true;
1292 this.pop(1);
1293 }
1294 else if (this.activeView) {
1295 cleanup(this.activeView, this.views, this.views, this.location, this.zone);
1296 }
1297 }
1298 getLastUrl(stackId) {
1299 const views = this.getStack(stackId);
1300 return views.length > 0 ? views[views.length - 1] : undefined;
1301 }
1302 /**
1303 * @internal
1304 */
1305 getRootUrl(stackId) {
1306 const views = this.getStack(stackId);
1307 return views.length > 0 ? views[0] : undefined;
1308 }
1309 getActiveStackId() {
1310 return this.activeView ? this.activeView.stackId : undefined;
1311 }
1312 /**
1313 * @internal
1314 */
1315 getActiveView() {
1316 return this.activeView;
1317 }
1318 hasRunningTask() {
1319 return this.runningTask !== undefined;
1320 }
1321 destroy() {
1322 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1323 this.containerEl = undefined;
1324 this.views.forEach(destroyView);
1325 this.activeView = undefined;
1326 this.views = [];
1327 }
1328 getStack(stackId) {
1329 return this.views.filter((v) => v.stackId === stackId);
1330 }
1331 insertView(enteringView, direction) {
1332 this.activeView = enteringView;
1333 this.views = insertView(this.views, enteringView, direction);
1334 return this.views.slice();
1335 }
1336 transition(enteringView, leavingView, direction, showGoBack, progressAnimation, animationBuilder) {
1337 if (this.skipTransition) {
1338 this.skipTransition = false;
1339 return Promise.resolve(false);
1340 }
1341 if (leavingView === enteringView) {
1342 return Promise.resolve(false);
1343 }
1344 const enteringEl = enteringView ? enteringView.element : undefined;
1345 const leavingEl = leavingView ? leavingView.element : undefined;
1346 const containerEl = this.containerEl;
1347 if (enteringEl && enteringEl !== leavingEl) {
1348 enteringEl.classList.add('ion-page');
1349 enteringEl.classList.add('ion-page-invisible');
1350 if (enteringEl.parentElement !== containerEl) {
1351 containerEl.appendChild(enteringEl);
1352 }
1353 if (containerEl.commit) {
1354 return containerEl.commit(enteringEl, leavingEl, {
1355 duration: direction === undefined ? 0 : undefined,
1356 direction,
1357 showGoBack,
1358 progressAnimation,
1359 animationBuilder,
1360 });
1361 }
1362 }
1363 return Promise.resolve(false);
1364 }
1365 async wait(task) {
1366 if (this.runningTask !== undefined) {
1367 await this.runningTask;
1368 this.runningTask = undefined;
1369 }
1370 const promise = (this.runningTask = task());
1371 promise.finally(() => (this.runningTask = undefined));
1372 return promise;
1373 }
1374}
1375const cleanupAsync = (activeRoute, views, viewsSnapshot, location, zone) => {
1376 if (typeof requestAnimationFrame === 'function') {
1377 return new Promise((resolve) => {
1378 requestAnimationFrame(() => {
1379 cleanup(activeRoute, views, viewsSnapshot, location, zone);
1380 resolve();
1381 });
1382 });
1383 }
1384 return Promise.resolve();
1385};
1386const cleanup = (activeRoute, views, viewsSnapshot, location, zone) => {
1387 /**
1388 * Re-enter the Angular zone when destroying page components. This will allow
1389 * lifecycle events (`ngOnDestroy`) to be run inside the Angular zone.
1390 */
1391 zone.run(() => viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView));
1392 views.forEach((view) => {
1393 /**
1394 * In the event that a user navigated multiple
1395 * times in rapid succession, we want to make sure
1396 * we don't pre-emptively detach a view while
1397 * it is in mid-transition.
1398 *
1399 * In this instance we also do not care about query
1400 * params or fragments as it will be the same view regardless
1401 */
1402 const locationWithoutParams = location.path().split('?')[0];
1403 const locationWithoutFragment = locationWithoutParams.split('#')[0];
1404 if (view !== activeRoute && view.url !== locationWithoutFragment) {
1405 const element = view.element;
1406 element.setAttribute('aria-hidden', 'true');
1407 element.classList.add('ion-page-hidden');
1408 view.ref.changeDetectorRef.detach();
1409 }
1410 });
1411};
1412
1413// TODO(FW-2827): types
1414// eslint-disable-next-line @angular-eslint/directive-class-suffix
1415class IonRouterOutlet {
1416 constructor(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet) {
1417 this.parentOutlet = parentOutlet;
1418 this.activatedView = null;
1419 // Maintain map of activated route proxies for each component instance
1420 this.proxyMap = new WeakMap();
1421 // Keep the latest activated route in a subject for the proxy routes to switch map to
1422 this.currentActivatedRoute$ = new BehaviorSubject(null);
1423 this.activated = null;
1424 this._activatedRoute = null;
1425 /**
1426 * The name of the outlet
1427 */
1428 this.name = PRIMARY_OUTLET;
1429 /** @internal */
1430 this.stackWillChange = new EventEmitter();
1431 /** @internal */
1432 this.stackDidChange = new EventEmitter();
1433 // eslint-disable-next-line @angular-eslint/no-output-rename
1434 this.activateEvents = new EventEmitter();
1435 // eslint-disable-next-line @angular-eslint/no-output-rename
1436 this.deactivateEvents = new EventEmitter();
1437 this.parentContexts = inject(ChildrenOutletContexts);
1438 this.location = inject(ViewContainerRef);
1439 this.environmentInjector = inject(EnvironmentInjector);
1440 this.inputBinder = inject(INPUT_BINDER, { optional: true });
1441 /** @nodoc */
1442 this.supportsBindingToComponentInputs = true;
1443 // Ionic providers
1444 this.config = inject(Config);
1445 this.navCtrl = inject(NavController);
1446 this.nativeEl = elementRef.nativeElement;
1447 this.name = name || PRIMARY_OUTLET;
1448 this.tabsPrefix = tabs === 'true' ? getUrl(router, activatedRoute) : undefined;
1449 this.stackCtrl = new StackController(this.tabsPrefix, this.nativeEl, router, this.navCtrl, zone, commonLocation);
1450 this.parentContexts.onChildOutletCreated(this.name, this);
1451 }
1452 /** @internal */
1453 get activatedComponentRef() {
1454 return this.activated;
1455 }
1456 set animation(animation) {
1457 this.nativeEl.animation = animation;
1458 }
1459 set animated(animated) {
1460 this.nativeEl.animated = animated;
1461 }
1462 set swipeGesture(swipe) {
1463 this._swipeGesture = swipe;
1464 this.nativeEl.swipeHandler = swipe
1465 ? {
1466 canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(),
1467 onStart: () => this.stackCtrl.startBackTransition(),
1468 onEnd: (shouldContinue) => this.stackCtrl.endBackTransition(shouldContinue),
1469 }
1470 : undefined;
1471 }
1472 ngOnDestroy() {
1473 this.stackCtrl.destroy();
1474 this.inputBinder?.unsubscribeFromRouteData(this);
1475 }
1476 getContext() {
1477 return this.parentContexts.getContext(this.name);
1478 }
1479 ngOnInit() {
1480 this.initializeOutletWithName();
1481 }
1482 // Note: Ionic deviates from the Angular Router implementation here
1483 initializeOutletWithName() {
1484 if (!this.activated) {
1485 // If the outlet was not instantiated at the time the route got activated we need to populate
1486 // the outlet when it is initialized (ie inside a NgIf)
1487 const context = this.getContext();
1488 if (context?.route) {
1489 this.activateWith(context.route, context.injector);
1490 }
1491 }
1492 new Promise((resolve) => componentOnReady(this.nativeEl, resolve)).then(() => {
1493 if (this._swipeGesture === undefined) {
1494 this.swipeGesture = this.config.getBoolean('swipeBackEnabled', this.nativeEl.mode === 'ios');
1495 }
1496 });
1497 }
1498 get isActivated() {
1499 return !!this.activated;
1500 }
1501 get component() {
1502 if (!this.activated) {
1503 throw new Error('Outlet is not activated');
1504 }
1505 return this.activated.instance;
1506 }
1507 get activatedRoute() {
1508 if (!this.activated) {
1509 throw new Error('Outlet is not activated');
1510 }
1511 return this._activatedRoute;
1512 }
1513 get activatedRouteData() {
1514 if (this._activatedRoute) {
1515 return this._activatedRoute.snapshot.data;
1516 }
1517 return {};
1518 }
1519 /**
1520 * Called when the `RouteReuseStrategy` instructs to detach the subtree
1521 */
1522 detach() {
1523 throw new Error('incompatible reuse strategy');
1524 }
1525 /**
1526 * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
1527 */
1528 // eslint-disable-next-line @typescript-eslint/no-unused-vars
1529 attach(_ref, _activatedRoute) {
1530 throw new Error('incompatible reuse strategy');
1531 }
1532 deactivate() {
1533 if (this.activated) {
1534 if (this.activatedView) {
1535 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1536 const context = this.getContext();
1537 this.activatedView.savedData = new Map(context.children['contexts']);
1538 /**
1539 * Angular v11.2.10 introduced a change
1540 * where this route context is cleared out when
1541 * a router-outlet is deactivated, However,
1542 * we need this route information in order to
1543 * return a user back to the correct tab when
1544 * leaving and then going back to the tab context.
1545 */
1546 const primaryOutlet = this.activatedView.savedData.get('primary');
1547 if (primaryOutlet && context.route) {
1548 primaryOutlet.route = { ...context.route };
1549 }
1550 /**
1551 * Ensure we are saving the NavigationExtras
1552 * data otherwise it will be lost
1553 */
1554 this.activatedView.savedExtras = {};
1555 if (context.route) {
1556 const contextSnapshot = context.route.snapshot;
1557 this.activatedView.savedExtras.queryParams = contextSnapshot.queryParams;
1558 this.activatedView.savedExtras.fragment = contextSnapshot.fragment;
1559 }
1560 }
1561 const c = this.component;
1562 this.activatedView = null;
1563 this.activated = null;
1564 this._activatedRoute = null;
1565 this.deactivateEvents.emit(c);
1566 }
1567 }
1568 activateWith(activatedRoute, environmentInjector) {
1569 if (this.isActivated) {
1570 throw new Error('Cannot activate an already activated outlet');
1571 }
1572 this._activatedRoute = activatedRoute;
1573 let cmpRef;
1574 let enteringView = this.stackCtrl.getExistingView(activatedRoute);
1575 if (enteringView) {
1576 cmpRef = this.activated = enteringView.ref;
1577 const saved = enteringView.savedData;
1578 if (saved) {
1579 // self-restore
1580 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1581 const context = this.getContext();
1582 context.children['contexts'] = saved;
1583 }
1584 // Updated activated route proxy for this component
1585 this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute);
1586 }
1587 else {
1588 const snapshot = activatedRoute._futureSnapshot;
1589 /**
1590 * Angular 14 introduces a new `loadComponent` property to the route config.
1591 * This function will assign a `component` property to the route snapshot.
1592 * We check for the presence of this property to determine if the route is
1593 * using standalone components.
1594 */
1595 const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
1596 // We create an activated route proxy object that will maintain future updates for this component
1597 // over its lifecycle in the stack.
1598 const component$ = new BehaviorSubject(null);
1599 const activatedRouteProxy = this.createActivatedRouteProxy(component$, activatedRoute);
1600 const injector = new OutletInjector(activatedRouteProxy, childContexts, this.location.injector);
1601 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1602 const component = snapshot.routeConfig.component ?? snapshot.component;
1603 cmpRef = this.activated = this.location.createComponent(component, {
1604 index: this.location.length,
1605 injector,
1606 environmentInjector: environmentInjector ?? this.environmentInjector,
1607 });
1608 // Once the component is created we can push it to our local subject supplied to the proxy
1609 component$.next(cmpRef.instance);
1610 // Calling `markForCheck` to make sure we will run the change detection when the
1611 // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
1612 enteringView = this.stackCtrl.createView(this.activated, activatedRoute);
1613 // Store references to the proxy by component
1614 this.proxyMap.set(cmpRef.instance, activatedRouteProxy);
1615 this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute });
1616 }
1617 this.inputBinder?.bindActivatedRouteToOutletComponent(this);
1618 this.activatedView = enteringView;
1619 /**
1620 * The top outlet is set prior to the entering view's transition completing,
1621 * so that when we have nested outlets (e.g. ion-tabs inside an ion-router-outlet),
1622 * the tabs outlet will be assigned as the top outlet when a view inside tabs is
1623 * activated.
1624 *
1625 * In this scenario, activeWith is called for both the tabs and the root router outlet.
1626 * To avoid a race condition, we assign the top outlet synchronously.
1627 */
1628 this.navCtrl.setTopOutlet(this);
1629 const leavingView = this.stackCtrl.getActiveView();
1630 this.stackWillChange.emit({
1631 enteringView,
1632 tabSwitch: isTabSwitch(enteringView, leavingView),
1633 });
1634 this.stackCtrl.setActive(enteringView).then((data) => {
1635 this.activateEvents.emit(cmpRef.instance);
1636 this.stackDidChange.emit(data);
1637 });
1638 }
1639 /**
1640 * Returns `true` if there are pages in the stack to go back.
1641 */
1642 canGoBack(deep = 1, stackId) {
1643 return this.stackCtrl.canGoBack(deep, stackId);
1644 }
1645 /**
1646 * Resolves to `true` if it the outlet was able to sucessfully pop the last N pages.
1647 */
1648 pop(deep = 1, stackId) {
1649 return this.stackCtrl.pop(deep, stackId);
1650 }
1651 /**
1652 * Returns the URL of the active page of each stack.
1653 */
1654 getLastUrl(stackId) {
1655 const active = this.stackCtrl.getLastUrl(stackId);
1656 return active ? active.url : undefined;
1657 }
1658 /**
1659 * Returns the RouteView of the active page of each stack.
1660 * @internal
1661 */
1662 getLastRouteView(stackId) {
1663 return this.stackCtrl.getLastUrl(stackId);
1664 }
1665 /**
1666 * Returns the root view in the tab stack.
1667 * @internal
1668 */
1669 getRootView(stackId) {
1670 return this.stackCtrl.getRootUrl(stackId);
1671 }
1672 /**
1673 * Returns the active stack ID. In the context of ion-tabs, it means the active tab.
1674 */
1675 getActiveStackId() {
1676 return this.stackCtrl.getActiveStackId();
1677 }
1678 /**
1679 * Since the activated route can change over the life time of a component in an ion router outlet, we create
1680 * a proxy so that we can update the values over time as a user navigates back to components already in the stack.
1681 */
1682 createActivatedRouteProxy(component$, activatedRoute) {
1683 const proxy = new ActivatedRoute();
1684 proxy._futureSnapshot = activatedRoute._futureSnapshot;
1685 proxy._routerState = activatedRoute._routerState;
1686 proxy.snapshot = activatedRoute.snapshot;
1687 proxy.outlet = activatedRoute.outlet;
1688 proxy.component = activatedRoute.component;
1689 // Setup wrappers for the observables so consumers don't have to worry about switching to new observables as the state updates
1690 proxy._paramMap = this.proxyObservable(component$, 'paramMap');
1691 proxy._queryParamMap = this.proxyObservable(component$, 'queryParamMap');
1692 proxy.url = this.proxyObservable(component$, 'url');
1693 proxy.params = this.proxyObservable(component$, 'params');
1694 proxy.queryParams = this.proxyObservable(component$, 'queryParams');
1695 proxy.fragment = this.proxyObservable(component$, 'fragment');
1696 proxy.data = this.proxyObservable(component$, 'data');
1697 return proxy;
1698 }
1699 /**
1700 * Create a wrapped observable that will switch to the latest activated route matched by the given component
1701 */
1702 proxyObservable(component$, path) {
1703 return component$.pipe(
1704 // First wait until the component instance is pushed
1705 filter((component) => !!component), switchMap((component) => this.currentActivatedRoute$.pipe(filter((current) => current !== null && current.component === component), switchMap((current) => current && current.activatedRoute[path]), distinctUntilChanged())));
1706 }
1707 /**
1708 * Updates the activated route proxy for the given component to the new incoming router state
1709 */
1710 updateActivatedRouteProxy(component, activatedRoute) {
1711 const proxy = this.proxyMap.get(component);
1712 if (!proxy) {
1713 throw new Error(`Could not find activated route proxy for view`);
1714 }
1715 proxy._futureSnapshot = activatedRoute._futureSnapshot;
1716 proxy._routerState = activatedRoute._routerState;
1717 proxy.snapshot = activatedRoute.snapshot;
1718 proxy.outlet = activatedRoute.outlet;
1719 proxy.component = activatedRoute.component;
1720 this.currentActivatedRoute$.next({ component, activatedRoute });
1721 }
1722}
1723/** @nocollapse */ IonRouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.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 });
1724/** @nocollapse */ IonRouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.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 });
1725i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonRouterOutlet, decorators: [{
1726 type: Directive,
1727 args: [{
1728 selector: 'ion-router-outlet',
1729 exportAs: 'outlet',
1730 // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
1731 inputs: ['animated', 'animation', 'mode', 'swipeGesture'],
1732 }]
1733 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
1734 type: Attribute,
1735 args: ['name']
1736 }] }, { type: undefined, decorators: [{
1737 type: Optional
1738 }, {
1739 type: Attribute,
1740 args: ['tabs']
1741 }] }, { type: i1.Location }, { type: i0.ElementRef }, { type: i3.Router }, { type: i0.NgZone }, { type: i3.ActivatedRoute }, { type: IonRouterOutlet, decorators: [{
1742 type: SkipSelf
1743 }, {
1744 type: Optional
1745 }] }]; }, propDecorators: { name: [{
1746 type: Input
1747 }], stackWillChange: [{
1748 type: Output
1749 }], stackDidChange: [{
1750 type: Output
1751 }], activateEvents: [{
1752 type: Output,
1753 args: ['activate']
1754 }], deactivateEvents: [{
1755 type: Output,
1756 args: ['deactivate']
1757 }] } });
1758class OutletInjector {
1759 constructor(route, childContexts, parent) {
1760 this.route = route;
1761 this.childContexts = childContexts;
1762 this.parent = parent;
1763 }
1764 get(token, notFoundValue) {
1765 if (token === ActivatedRoute) {
1766 return this.route;
1767 }
1768 if (token === ChildrenOutletContexts) {
1769 return this.childContexts;
1770 }
1771 return this.parent.get(token, notFoundValue);
1772 }
1773}
1774// TODO: FW-4785 - Remove this once Angular 15 support is dropped
1775const INPUT_BINDER = new InjectionToken('');
1776/**
1777 * Injectable used as a tree-shakable provider for opting in to binding router data to component
1778 * inputs.
1779 *
1780 * The RouterOutlet registers itself with this service when an `ActivatedRoute` is attached or
1781 * activated. When this happens, the service subscribes to the `ActivatedRoute` observables (params,
1782 * queryParams, data) and sets the inputs of the component using `ComponentRef.setInput`.
1783 * Importantly, when an input does not have an item in the route data with a matching key, this
1784 * input is set to `undefined`. If it were not done this way, the previous information would be
1785 * retained if the data got removed from the route (i.e. if a query parameter is removed).
1786 *
1787 * The `RouterOutlet` should unregister itself when destroyed via `unsubscribeFromRouteData` so that
1788 * the subscriptions are cleaned up.
1789 */
1790class RoutedComponentInputBinder {
1791 constructor() {
1792 this.outletDataSubscriptions = new Map();
1793 }
1794 bindActivatedRouteToOutletComponent(outlet) {
1795 this.unsubscribeFromRouteData(outlet);
1796 this.subscribeToRouteData(outlet);
1797 }
1798 unsubscribeFromRouteData(outlet) {
1799 this.outletDataSubscriptions.get(outlet)?.unsubscribe();
1800 this.outletDataSubscriptions.delete(outlet);
1801 }
1802 subscribeToRouteData(outlet) {
1803 const { activatedRoute } = outlet;
1804 const dataSubscription = combineLatest([activatedRoute.queryParams, activatedRoute.params, activatedRoute.data])
1805 .pipe(switchMap(([queryParams, params, data], index) => {
1806 data = { ...queryParams, ...params, ...data };
1807 // Get the first result from the data subscription synchronously so it's available to
1808 // the component as soon as possible (and doesn't require a second change detection).
1809 if (index === 0) {
1810 return of(data);
1811 }
1812 // Promise.resolve is used to avoid synchronously writing the wrong data when
1813 // two of the Observables in the `combineLatest` stream emit one after
1814 // another.
1815 return Promise.resolve(data);
1816 }))
1817 .subscribe((data) => {
1818 // Outlet may have been deactivated or changed names to be associated with a different
1819 // route
1820 if (!outlet.isActivated ||
1821 !outlet.activatedComponentRef ||
1822 outlet.activatedRoute !== activatedRoute ||
1823 activatedRoute.component === null) {
1824 this.unsubscribeFromRouteData(outlet);
1825 return;
1826 }
1827 const mirror = reflectComponentType(activatedRoute.component);
1828 if (!mirror) {
1829 this.unsubscribeFromRouteData(outlet);
1830 return;
1831 }
1832 for (const { templateName } of mirror.inputs) {
1833 outlet.activatedComponentRef.setInput(templateName, data[templateName]);
1834 }
1835 });
1836 this.outletDataSubscriptions.set(outlet, dataSubscription);
1837 }
1838}
1839/** @nocollapse */ RoutedComponentInputBinder.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutedComponentInputBinder, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1840/** @nocollapse */ RoutedComponentInputBinder.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutedComponentInputBinder });
1841i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutedComponentInputBinder, decorators: [{
1842 type: Injectable
1843 }] });
1844const provideComponentInputBinding = () => {
1845 return {
1846 provide: INPUT_BINDER,
1847 useFactory: componentInputBindingFactory,
1848 deps: [Router],
1849 };
1850};
1851function componentInputBindingFactory(router) {
1852 /**
1853 * We cast the router to any here, since the componentInputBindingEnabled
1854 * property is not available until Angular v16.
1855 */
1856 if (router?.componentInputBindingEnabled) {
1857 return new RoutedComponentInputBinder();
1858 }
1859 return null;
1860}
1861
1862const BACK_BUTTON_INPUTS = ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'routerAnimation', 'text', 'type'];
1863let IonBackButton = class IonBackButton {
1864 constructor(routerOutlet, navCtrl, config, r, z, c) {
1865 this.routerOutlet = routerOutlet;
1866 this.navCtrl = navCtrl;
1867 this.config = config;
1868 this.r = r;
1869 this.z = z;
1870 c.detach();
1871 this.el = this.r.nativeElement;
1872 }
1873 /**
1874 * @internal
1875 */
1876 onClick(ev) {
1877 const defaultHref = this.defaultHref || this.config.get('backButtonDefaultHref');
1878 if (this.routerOutlet?.canGoBack()) {
1879 this.navCtrl.setDirection('back', undefined, undefined, this.routerAnimation);
1880 this.routerOutlet.pop();
1881 ev.preventDefault();
1882 }
1883 else if (defaultHref != null) {
1884 this.navCtrl.navigateBack(defaultHref, { animation: this.routerAnimation });
1885 ev.preventDefault();
1886 }
1887 }
1888};
1889/** @nocollapse */ IonBackButton.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.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 });
1890/** @nocollapse */ IonBackButton.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.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 });
1891IonBackButton = __decorate([
1892 ProxyCmp({
1893 inputs: BACK_BUTTON_INPUTS,
1894 })
1895], IonBackButton);
1896i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonBackButton, decorators: [{
1897 type: Directive,
1898 args: [{
1899 // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
1900 inputs: BACK_BUTTON_INPUTS,
1901 }]
1902 }], ctorParameters: function () { return [{ type: IonRouterOutlet, decorators: [{
1903 type: Optional
1904 }] }, { type: NavController }, { type: Config }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { defaultHref: [{
1905 type: Input
1906 }], routerAnimation: [{
1907 type: Input
1908 }], onClick: [{
1909 type: HostListener,
1910 args: ['click', ['$event']]
1911 }] } });
1912
1913/**
1914 * Adds support for Ionic routing directions and animations to the base Angular router link directive.
1915 *
1916 * When the router link is clicked, the directive will assign the direction and
1917 * animation so that the routing integration will transition correctly.
1918 */
1919class RouterLinkDelegateDirective {
1920 constructor(locationStrategy, navCtrl, elementRef, router, routerLink) {
1921 this.locationStrategy = locationStrategy;
1922 this.navCtrl = navCtrl;
1923 this.elementRef = elementRef;
1924 this.router = router;
1925 this.routerLink = routerLink;
1926 this.routerDirection = 'forward';
1927 }
1928 ngOnInit() {
1929 this.updateTargetUrlAndHref();
1930 }
1931 ngOnChanges() {
1932 this.updateTargetUrlAndHref();
1933 }
1934 updateTargetUrlAndHref() {
1935 if (this.routerLink?.urlTree) {
1936 const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree));
1937 this.elementRef.nativeElement.href = href;
1938 }
1939 }
1940 /**
1941 * @internal
1942 */
1943 onClick(ev) {
1944 this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
1945 /**
1946 * This prevents the browser from
1947 * performing a page reload when pressing
1948 * an Ionic component with routerLink.
1949 * The page reload interferes with routing
1950 * and causes ion-back-button to disappear
1951 * since the local history is wiped on reload.
1952 */
1953 ev.preventDefault();
1954 }
1955}
1956/** @nocollapse */ RouterLinkDelegateDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.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 });
1957/** @nocollapse */ RouterLinkDelegateDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: RouterLinkDelegateDirective, selector: ":not(a):not(area)[routerLink]", inputs: { routerDirection: "routerDirection", routerAnimation: "routerAnimation" }, host: { listeners: { "click": "onClick($event)" } }, usesOnChanges: true, ngImport: i0 });
1958i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterLinkDelegateDirective, decorators: [{
1959 type: Directive,
1960 args: [{
1961 selector: ':not(a):not(area)[routerLink]',
1962 }]
1963 }], ctorParameters: function () { return [{ type: i1.LocationStrategy }, { type: NavController }, { type: i0.ElementRef }, { type: i3.Router }, { type: i3.RouterLink, decorators: [{
1964 type: Optional
1965 }] }]; }, propDecorators: { routerDirection: [{
1966 type: Input
1967 }], routerAnimation: [{
1968 type: Input
1969 }], onClick: [{
1970 type: HostListener,
1971 args: ['click', ['$event']]
1972 }] } });
1973class RouterLinkWithHrefDelegateDirective {
1974 constructor(locationStrategy, navCtrl, elementRef, router, routerLink) {
1975 this.locationStrategy = locationStrategy;
1976 this.navCtrl = navCtrl;
1977 this.elementRef = elementRef;
1978 this.router = router;
1979 this.routerLink = routerLink;
1980 this.routerDirection = 'forward';
1981 }
1982 ngOnInit() {
1983 this.updateTargetUrlAndHref();
1984 }
1985 ngOnChanges() {
1986 this.updateTargetUrlAndHref();
1987 }
1988 updateTargetUrlAndHref() {
1989 if (this.routerLink?.urlTree) {
1990 const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree));
1991 this.elementRef.nativeElement.href = href;
1992 }
1993 }
1994 /**
1995 * @internal
1996 */
1997 onClick() {
1998 this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
1999 }
2000}
2001/** @nocollapse */ RouterLinkWithHrefDelegateDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.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 });
2002/** @nocollapse */ RouterLinkWithHrefDelegateDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: RouterLinkWithHrefDelegateDirective, selector: "a[routerLink],area[routerLink]", inputs: { routerDirection: "routerDirection", routerAnimation: "routerAnimation" }, host: { listeners: { "click": "onClick()" } }, usesOnChanges: true, ngImport: i0 });
2003i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterLinkWithHrefDelegateDirective, decorators: [{
2004 type: Directive,
2005 args: [{
2006 selector: 'a[routerLink],area[routerLink]',
2007 }]
2008 }], ctorParameters: function () { return [{ type: i1.LocationStrategy }, { type: NavController }, { type: i0.ElementRef }, { type: i3.Router }, { type: i3.RouterLink, decorators: [{
2009 type: Optional
2010 }] }]; }, propDecorators: { routerDirection: [{
2011 type: Input
2012 }], routerAnimation: [{
2013 type: Input
2014 }], onClick: [{
2015 type: HostListener,
2016 args: ['click']
2017 }] } });
2018
2019const NAV_INPUTS = ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'];
2020const NAV_METHODS = [
2021 'push',
2022 'insert',
2023 'insertPages',
2024 'pop',
2025 'popTo',
2026 'popToRoot',
2027 'removeIndex',
2028 'setRoot',
2029 'setPages',
2030 'getActive',
2031 'getByIndex',
2032 'canGoBack',
2033 'getPrevious',
2034];
2035let IonNav = class IonNav {
2036 constructor(ref, environmentInjector, injector, angularDelegate, z, c) {
2037 this.z = z;
2038 c.detach();
2039 this.el = ref.nativeElement;
2040 ref.nativeElement.delegate = angularDelegate.create(environmentInjector, injector);
2041 proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);
2042 }
2043};
2044/** @nocollapse */ IonNav.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.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 });
2045/** @nocollapse */ IonNav.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: IonNav, inputs: { animated: "animated", animation: "animation", root: "root", rootParams: "rootParams", swipeGesture: "swipeGesture" }, ngImport: i0 });
2046IonNav = __decorate([
2047 ProxyCmp({
2048 inputs: NAV_INPUTS,
2049 methods: NAV_METHODS,
2050 })
2051], IonNav);
2052i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonNav, decorators: [{
2053 type: Directive,
2054 args: [{
2055 // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
2056 inputs: NAV_INPUTS,
2057 }]
2058 }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.EnvironmentInjector }, { type: i0.Injector }, { type: AngularDelegate }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }]; } });
2059
2060// eslint-disable-next-line @angular-eslint/directive-class-suffix
2061class IonTabs {
2062 constructor(navCtrl) {
2063 this.navCtrl = navCtrl;
2064 /**
2065 * Emitted before the tab view is changed.
2066 */
2067 this.ionTabsWillChange = new EventEmitter();
2068 /**
2069 * Emitted after the tab view is changed.
2070 */
2071 this.ionTabsDidChange = new EventEmitter();
2072 this.tabBarSlot = 'bottom';
2073 }
2074 ngAfterContentInit() {
2075 this.detectSlotChanges();
2076 }
2077 ngAfterContentChecked() {
2078 this.detectSlotChanges();
2079 }
2080 /**
2081 * @internal
2082 */
2083 onStackWillChange({ enteringView, tabSwitch }) {
2084 const stackId = enteringView.stackId;
2085 if (tabSwitch && stackId !== undefined) {
2086 this.ionTabsWillChange.emit({ tab: stackId });
2087 }
2088 }
2089 /**
2090 * @internal
2091 */
2092 onStackDidChange({ enteringView, tabSwitch }) {
2093 const stackId = enteringView.stackId;
2094 if (tabSwitch && stackId !== undefined) {
2095 if (this.tabBar) {
2096 this.tabBar.selectedTab = stackId;
2097 }
2098 this.ionTabsDidChange.emit({ tab: stackId });
2099 }
2100 }
2101 /**
2102 * When a tab button is clicked, there are several scenarios:
2103 * 1. If the selected tab is currently active (the tab button has been clicked
2104 * again), then it should go to the root view for that tab.
2105 *
2106 * a. Get the saved root view from the router outlet. If the saved root view
2107 * matches the tabRootUrl, set the route view to this view including the
2108 * navigation extras.
2109 * b. If the saved root view from the router outlet does
2110 * not match, navigate to the tabRootUrl. No navigation extras are
2111 * included.
2112 *
2113 * 2. If the current tab tab is not currently selected, get the last route
2114 * view from the router outlet.
2115 *
2116 * a. If the last route view exists, navigate to that view including any
2117 * navigation extras
2118 * b. If the last route view doesn't exist, then navigate
2119 * to the default tabRootUrl
2120 */
2121 select(tabOrEvent) {
2122 const isTabString = typeof tabOrEvent === 'string';
2123 const tab = isTabString ? tabOrEvent : tabOrEvent.detail.tab;
2124 const alreadySelected = this.outlet.getActiveStackId() === tab;
2125 const tabRootUrl = `${this.outlet.tabsPrefix}/${tab}`;
2126 /**
2127 * If this is a nested tab, prevent the event
2128 * from bubbling otherwise the outer tabs
2129 * will respond to this event too, causing
2130 * the app to get directed to the wrong place.
2131 */
2132 if (!isTabString) {
2133 tabOrEvent.stopPropagation();
2134 }
2135 if (alreadySelected) {
2136 const activeStackId = this.outlet.getActiveStackId();
2137 const activeView = this.outlet.getLastRouteView(activeStackId);
2138 // If on root tab, do not navigate to root tab again
2139 if (activeView?.url === tabRootUrl) {
2140 return;
2141 }
2142 const rootView = this.outlet.getRootView(tab);
2143 const navigationExtras = rootView && tabRootUrl === rootView.url && rootView.savedExtras;
2144 return this.navCtrl.navigateRoot(tabRootUrl, {
2145 ...navigationExtras,
2146 animated: true,
2147 animationDirection: 'back',
2148 });
2149 }
2150 else {
2151 const lastRoute = this.outlet.getLastRouteView(tab);
2152 /**
2153 * If there is a lastRoute, goto that, otherwise goto the fallback url of the
2154 * selected tab
2155 */
2156 const url = lastRoute?.url || tabRootUrl;
2157 const navigationExtras = lastRoute?.savedExtras;
2158 return this.navCtrl.navigateRoot(url, {
2159 ...navigationExtras,
2160 animated: true,
2161 animationDirection: 'back',
2162 });
2163 }
2164 }
2165 getSelected() {
2166 return this.outlet.getActiveStackId();
2167 }
2168 /**
2169 * Detects changes to the slot attribute of the tab bar.
2170 *
2171 * If the slot attribute has changed, then the tab bar
2172 * should be relocated to the new slot position.
2173 */
2174 detectSlotChanges() {
2175 this.tabBars.forEach((tabBar) => {
2176 // el is a protected attribute from the generated component wrapper
2177 const currentSlot = tabBar.el.getAttribute('slot');
2178 if (currentSlot !== this.tabBarSlot) {
2179 this.tabBarSlot = currentSlot;
2180 this.relocateTabBar();
2181 }
2182 });
2183 }
2184 /**
2185 * Relocates the tab bar to the new slot position.
2186 */
2187 relocateTabBar() {
2188 /**
2189 * `el` is a protected attribute from the generated component wrapper.
2190 * To avoid having to manually create the wrapper for tab bar, we
2191 * cast the tab bar to any and access the protected attribute.
2192 */
2193 const tabBar = this.tabBar.el;
2194 if (this.tabBarSlot === 'top') {
2195 /**
2196 * A tab bar with a slot of "top" should be inserted
2197 * at the top of the container.
2198 */
2199 this.tabsInner.nativeElement.before(tabBar);
2200 }
2201 else {
2202 /**
2203 * A tab bar with a slot of "bottom" or without a slot
2204 * should be inserted at the end of the container.
2205 */
2206 this.tabsInner.nativeElement.after(tabBar);
2207 }
2208 }
2209}
2210/** @nocollapse */ IonTabs.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonTabs, deps: [{ token: NavController }], target: i0.ɵɵFactoryTarget.Directive });
2211/** @nocollapse */ IonTabs.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.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 });
2212i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonTabs, decorators: [{
2213 type: Directive,
2214 args: [{
2215 selector: 'ion-tabs',
2216 }]
2217 }], ctorParameters: function () { return [{ type: NavController }]; }, propDecorators: { tabsInner: [{
2218 type: ViewChild,
2219 args: ['tabsInner', { read: ElementRef, static: true }]
2220 }], ionTabsWillChange: [{
2221 type: Output
2222 }], ionTabsDidChange: [{
2223 type: Output
2224 }], select: [{
2225 type: HostListener,
2226 args: ['ionTabButtonClick', ['$event']]
2227 }] } });
2228
2229const raf = (h) => {
2230 if (typeof __zone_symbol__requestAnimationFrame === 'function') {
2231 return __zone_symbol__requestAnimationFrame(h);
2232 }
2233 if (typeof requestAnimationFrame === 'function') {
2234 return requestAnimationFrame(h);
2235 }
2236 return setTimeout(h);
2237};
2238
2239// TODO(FW-2827): types
2240class ValueAccessor {
2241 constructor(injector, elementRef) {
2242 this.injector = injector;
2243 this.elementRef = elementRef;
2244 this.onChange = () => {
2245 /**/
2246 };
2247 this.onTouched = () => {
2248 /**/
2249 };
2250 }
2251 writeValue(value) {
2252 this.elementRef.nativeElement.value = this.lastValue = value;
2253 setIonicClasses(this.elementRef);
2254 }
2255 /**
2256 * Notifies the ControlValueAccessor of a change in the value of the control.
2257 *
2258 * This is called by each of the ValueAccessor directives when we want to update
2259 * the status and validity of the form control. For example with text components this
2260 * is called when the ionInput event is fired. For select components this is called
2261 * when the ionChange event is fired.
2262 *
2263 * This also updates the Ionic form status classes on the element.
2264 *
2265 * @param el The component element.
2266 * @param value The new value of the control.
2267 */
2268 handleValueChange(el, value) {
2269 if (el === this.elementRef.nativeElement) {
2270 if (value !== this.lastValue) {
2271 this.lastValue = value;
2272 this.onChange(value);
2273 }
2274 setIonicClasses(this.elementRef);
2275 }
2276 }
2277 _handleBlurEvent(el) {
2278 if (el === this.elementRef.nativeElement) {
2279 this.onTouched();
2280 setIonicClasses(this.elementRef);
2281 }
2282 }
2283 registerOnChange(fn) {
2284 this.onChange = fn;
2285 }
2286 registerOnTouched(fn) {
2287 this.onTouched = fn;
2288 }
2289 setDisabledState(isDisabled) {
2290 this.elementRef.nativeElement.disabled = isDisabled;
2291 }
2292 ngOnDestroy() {
2293 if (this.statusChanges) {
2294 this.statusChanges.unsubscribe();
2295 }
2296 }
2297 ngAfterViewInit() {
2298 let ngControl;
2299 try {
2300 ngControl = this.injector.get(NgControl);
2301 }
2302 catch {
2303 /* No FormControl or ngModel binding */
2304 }
2305 if (!ngControl) {
2306 return;
2307 }
2308 // Listen for changes in validity, disabled, or pending states
2309 if (ngControl.statusChanges) {
2310 this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.elementRef));
2311 }
2312 /**
2313 * TODO FW-2787: Remove this in favor of https://github.com/angular/angular/issues/10887
2314 * whenever it is implemented.
2315 */
2316 const formControl = ngControl.control;
2317 if (formControl) {
2318 const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
2319 methodsToPatch.forEach((method) => {
2320 if (typeof formControl[method] !== 'undefined') {
2321 const oldFn = formControl[method].bind(formControl);
2322 formControl[method] = (...params) => {
2323 oldFn(...params);
2324 setIonicClasses(this.elementRef);
2325 };
2326 }
2327 });
2328 }
2329 }
2330}
2331/** @nocollapse */ ValueAccessor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: ValueAccessor, deps: [{ token: i0.Injector }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
2332/** @nocollapse */ ValueAccessor.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: ValueAccessor, host: { listeners: { "ionBlur": "_handleBlurEvent($event.target)" } }, ngImport: i0 });
2333i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: ValueAccessor, decorators: [{
2334 type: Directive
2335 }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ElementRef }]; }, propDecorators: { _handleBlurEvent: [{
2336 type: HostListener,
2337 args: ['ionBlur', ['$event.target']]
2338 }] } });
2339const setIonicClasses = (element) => {
2340 raf(() => {
2341 const input = element.nativeElement;
2342 const hasValue = input.value != null && input.value.toString().length > 0;
2343 const classes = getClasses(input);
2344 setClasses(input, classes);
2345 const item = input.closest('ion-item');
2346 if (item) {
2347 if (hasValue) {
2348 setClasses(item, [...classes, 'item-has-value']);
2349 }
2350 else {
2351 setClasses(item, classes);
2352 }
2353 }
2354 });
2355};
2356const getClasses = (element) => {
2357 const classList = element.classList;
2358 const classes = [];
2359 for (let i = 0; i < classList.length; i++) {
2360 const item = classList.item(i);
2361 if (item !== null && startsWith(item, 'ng-')) {
2362 classes.push(`ion-${item.substring(3)}`);
2363 }
2364 }
2365 return classes;
2366};
2367const setClasses = (element, classes) => {
2368 const classList = element.classList;
2369 classList.remove('ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine');
2370 classList.add(...classes);
2371};
2372const startsWith = (input, search) => {
2373 return input.substring(0, search.length) === search;
2374};
2375
2376/**
2377 * Provides a way to customize when activated routes get reused.
2378 */
2379class IonicRouteStrategy {
2380 /**
2381 * Whether the given route should detach for later reuse.
2382 */
2383 shouldDetach(_route) {
2384 return false;
2385 }
2386 /**
2387 * Returns `false`, meaning the route (and its subtree) is never reattached
2388 */
2389 shouldAttach(_route) {
2390 return false;
2391 }
2392 /**
2393 * A no-op; the route is never stored since this strategy never detaches routes for later re-use.
2394 */
2395 store(_route, _detachedTree) {
2396 return;
2397 }
2398 /**
2399 * Returns `null` because this strategy does not store routes for later re-use.
2400 */
2401 retrieve(_route) {
2402 return null;
2403 }
2404 /**
2405 * Determines if a route should be reused.
2406 * This strategy returns `true` when the future route config and
2407 * current route config are identical and all route parameters are identical.
2408 */
2409 shouldReuseRoute(future, curr) {
2410 if (future.routeConfig !== curr.routeConfig) {
2411 return false;
2412 }
2413 // checking router params
2414 const futureParams = future.params;
2415 const currentParams = curr.params;
2416 const keysA = Object.keys(futureParams);
2417 const keysB = Object.keys(currentParams);
2418 if (keysA.length !== keysB.length) {
2419 return false;
2420 }
2421 // Test for A's keys different from B.
2422 for (const key of keysA) {
2423 if (currentParams[key] !== futureParams[key]) {
2424 return false;
2425 }
2426 }
2427 return true;
2428 }
2429}
2430
2431// TODO(FW-2827): types
2432class OverlayBaseController {
2433 constructor(ctrl) {
2434 this.ctrl = ctrl;
2435 }
2436 /**
2437 * Creates a new overlay
2438 */
2439 create(opts) {
2440 return this.ctrl.create((opts || {}));
2441 }
2442 /**
2443 * When `id` is not provided, it dismisses the top overlay.
2444 */
2445 dismiss(data, role, id) {
2446 return this.ctrl.dismiss(data, role, id);
2447 }
2448 /**
2449 * Returns the top overlay.
2450 */
2451 getTop() {
2452 return this.ctrl.getTop();
2453 }
2454}
2455
2456/**
2457 * Generated bundle index. Do not edit.
2458 */
2459
2460export { AngularDelegate, Config, ConfigToken, DomController, IonBackButton, IonModal, IonNav, IonPopover, IonRouterOutlet, IonTabs, IonicRouteStrategy, MenuController, NavController, NavParams, OverlayBaseController, Platform, ProxyCmp, RouterLinkDelegateDirective, RouterLinkWithHrefDelegateDirective, ValueAccessor, bindLifecycleEvents, provideComponentInputBinding, raf, setIonicClasses };
2461//# sourceMappingURL=ionic-angular-common.mjs.map