UNPKG

ngx-back-button

Version:

A library for handling a proper angular back button capability

200 lines (192 loc) 8.2 kB
import * as i0 from '@angular/core'; import { InjectionToken, inject, signal, Injectable, HostListener, Input, Directive, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core'; import { Location } from '@angular/common'; import { Router, NavigationEnd } from '@angular/router'; import { filter, skip } from 'rxjs'; const NgxBackButtonServiceProvider = new InjectionToken('NgxBackButtonServiceConfig'); class NgxBackButtonService { #router; #location; #rootConfig; constructor() { this.#router = inject(Router); this.#location = inject(Location); this.#rootConfig = inject(NgxBackButtonServiceProvider, { optional: true }); this._history = []; this._navigatingBack = false; this._$nextBackNavigationPath = signal(this._getFallBackNavigationPath(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "_$nextBackNavigationPath" }] : /* istanbul ignore next */ [])); this.$nextBackNavigationPath = this._$nextBackNavigationPath.asReadonly(); this.#router.events .pipe(filter((e) => e instanceof NavigationEnd), skip(1)) .subscribe((event) => { if (!this._navigatingBack) this._history.push(event.urlAfterRedirects); this._updateNextBackNavigationPath(); this._navigatingBack = false; }); } getHistory() { return this._history; } /** * @param fallback * @param config Optional configuration to override the root configuration (typically from child routes) * @return Boolean: True === Had an history to go back to */ back(fallback, config) { this._navigatingBack = true; const record = this._history.pop(); this._updateNextBackNavigationPath(fallback, config); if (this._history.length > 0) { this.#location.back(); return true; } else { this._navigatingBack = false; // Give an element to go back to on next navigation try { window.history.replaceState(null, '', this._getFallBackNavigationPath(fallback, config)); } catch (error) { console.error('NgxBackButton: ' + error); } window.history.pushState(null, '', record ?? this.#router.url); this.#location.back(); return false; } } _getFallBackNavigationPath(fallback, config) { const effectiveConfig = config || this.#rootConfig; const rootUrl = effectiveConfig?.rootUrl || ''; const fallbackPrefix = effectiveConfig?.fallbackPrefix || ''; const fallbackPath = fallback || rootUrl; if (fallbackPath.startsWith('../')) { return this._resolveRelativeFallbackNavigationPath(fallbackPath); } return fallbackPrefix + fallbackPath; } _resolveRelativeFallbackNavigationPath(fallback) { const suffixIndex = fallback.search(/[?#]/); const path = suffixIndex === -1 ? fallback : fallback.slice(0, suffixIndex); const suffix = suffixIndex === -1 ? '' : fallback.slice(suffixIndex); const fallbackSegments = path.split('/'); let parentSegmentCount = 0; while (fallbackSegments[0] === '..') { fallbackSegments.shift(); parentSegmentCount++; } const currentPath = this.#router.url.split(/[?#]/, 1)[0]; const currentSegments = currentPath.split('/').filter(Boolean); const retainedSegments = currentSegments.slice(0, Math.max(0, currentSegments.length - parentSegmentCount)); const appendedSegments = fallbackSegments.filter((segment) => segment && segment !== '.'); return `/${[...retainedSegments, ...appendedSegments].join('/')}${suffix}`; } _updateNextBackNavigationPath(fallback, config) { const previousHistoryEntry = this._history[this._history.length - 2]; this._$nextBackNavigationPath.set(previousHistoryEntry ?? this._getFallBackNavigationPath(fallback, config)); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxBackButtonService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxBackButtonService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxBackButtonService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [] }); class NgxBackButtonDirective { #ngxBackButtonService = inject(NgxBackButtonService); #config = inject(NgxBackButtonServiceProvider, { optional: true }); onClick() { this.#ngxBackButtonService.back(this.ngxBackButton, this.#config); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxBackButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: NgxBackButtonDirective, isStandalone: true, selector: "[ngxBackButton]", inputs: { ngxBackButton: "ngxBackButton" }, host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxBackButtonDirective, decorators: [{ type: Directive, args: [{ selector: '[ngxBackButton]', }] }], propDecorators: { ngxBackButton: [{ type: Input }], onClick: [{ type: HostListener, args: ['click'] }] } }); /** * Internal function to create environment providers for NgxBackButton configuration. * @internal */ function createNgxBackButtonProviders(config) { return makeEnvironmentProviders([ { provide: NgxBackButtonServiceProvider, useValue: config || {}, }, provideEnvironmentInitializer(() => inject(NgxBackButtonService)), ]); } /** * Provides the NgxBackButton service with the given configuration. * Use this in your root application providers. * * When both root and child configurations are provided, the child configuration * will override the root configuration for routes under that path, following * Angular's hierarchical dependency injection. * * @param config Configuration for the NgxBackButton service * @returns Environment providers for NgxBackButton * * @example * ```typescript * bootstrapApplication(AppComponent, { * providers: [ * provideNgxBackButton({ * rootUrl: '/home', * fallbackPrefix: '/tabs' * }) * ] * }) * ``` */ function provideNgxBackButton(config) { return createNgxBackButtonProviders(config); } /** * Provides child-level configuration for NgxBackButton service. * Use this in lazy-loaded route providers to override the root configuration. * * This configuration will override the root configuration for all components * and directives within the route's component tree, following Angular's * hierarchical dependency injection. * * @param config Configuration for the NgxBackButton service at child level * @returns Environment providers for NgxBackButton child configuration * * @example * ```typescript * export const routes: Routes = [ * { * path: 'child', * providers: [ * provideNgxBackButtonChild({ * rootUrl: '/login' * }) * ], * loadComponent: () => import('./child.component') * } * ] * ``` */ function provideNgxBackButtonChild(config) { return createNgxBackButtonProviders(config); } /* * Public API Surface of ngx-back-button */ /** * Generated bundle index. Do not edit. */ export { NgxBackButtonDirective, NgxBackButtonService, NgxBackButtonServiceProvider, provideNgxBackButton, provideNgxBackButtonChild }; //# sourceMappingURL=ngx-back-button.mjs.map