import { OnInit, CUSTOM_ELEMENTS_SCHEMA, Input, Component, inject, HostListener } from "@angular/core";
import { PredefinedColors } from "@ionic/core";
import { ForAngularModule } from 'src/lib/for-angular.module';
import { EventConstants, RouteDirections } from "src/lib/engine/constants";
import { StringOrBoolean } from "src/lib/engine/types";
import { stringToBoolean } from "src/lib/helpers/utils";
import { Location } from '@angular/common';
import { windowEventEmitter } from "src/lib/helpers/utils";
import { addIcons } from 'ionicons';
import { chevronBackOutline } from 'ionicons/icons';
import { FunctionLike } from "src/lib/engine/types";
@Component({
selector: 'app-back-button',
templateUrl: './back-button.component.html',
schemas: [CUSTOM_ELEMENTS_SCHEMA],
styleUrls: ['./back-button.component.scss'],
imports: [ForAngularModule],
standalone: true,
})
export class BackButtonComponent implements OnInit {
/**
* @description Controls whether default navigation behavior is prevented.
* @summary When set to true, clicking the back button will only emit events without
* performing actual navigation. This is useful for cases where you want to intercept
* the back action and handle it in a custom way, such as showing a confirmation dialog
* before navigating away from a form with unsaved changes.
*
* @type {StringOrBoolean}
* @default false
* @memberOf BackButtonComponent
*/
@Input()
preventDefault: StringOrBoolean = false;
/**
* @description Controls whether navigation events are emitted.
* @summary When set to true, the component will emit window events when navigation
* occurs. These events can be listened to by other components to react to navigation
* changes. For example, a component might listen for back navigation events to reset
* its state or update its display.
*
* @type {StringOrBoolean}
* @default true
* @memberOf BackButtonComponent
*/
@Input()
emitEvent: StringOrBoolean = true;
/**
* @description Custom navigation target for the back button.
* @summary Specifies where the back button should navigate to when clicked.
* This can be either a URL string or a function that handles navigation.
* When not provided, the component will navigate to the previous URL in the browser history.
* When a function is provided, it will be called instead of performing standard navigation.
*
* @type {(string | FunctionLike | undefined)}
* @memberOf BackButtonComponent
*/
@Input()
link?: string | FunctionLike;
/**
* @description The direction of navigation animation.
* @summary Controls the animation and transition behavior when navigating.
* This affects how the page transition appears to the user. For example,
* BACK typically slides the page from left to right, while FORWARD slides
* from right to left. This helps provide visual cues about navigation direction.
*
* @type {RouteDirections}
* @default RouteDirections.BACK
* @memberOf BackButtonComponent
*/
@Input()
direction: RouteDirections = RouteDirections.BACK;
/**
* @description Determines if text should be displayed alongside the icon.
* @summary When set to true, the component will display text next to the icon.
* This text can be customized using the text property. Displaying text can
* make the button's purpose clearer to users, especially for accessibility.
*
* @type {StringOrBoolean}
* @default false
* @memberOf BackButtonComponent
*/
@Input()
showText: StringOrBoolean = false;
/**
* @description Text to display when showText is true.
* @summary Specifies the text content to show next to the icon when showText is enabled.
* This can be a direct string or a translation key. By default, it shows "back".
* The text can be localized based on the component's locale settings.
*
* @type {string}
* @default 'back'
* @memberOf BackButtonComponent
*/
@Input()
text?: string = 'back';
/**
* @description Color of the back button.
* @summary Sets the color of the back button using Ionic's predefined color palette.
* This allows the button to match the application's color scheme. Common values
* include 'primary', 'secondary', 'tertiary', 'success', 'warning', 'danger', etc.
*
* @type {PredefinedColors}
* @default "primary"
* @memberOf BackButtonComponent
*/
@Input()
color: PredefinedColors = "primary";
/**
* @description Color of the toolbar containing the back button.
* @summary When set, this property changes the button color to 'light' for better contrast
* against colored toolbars. This is useful when placing the back button in a colored
* header or toolbar, ensuring it remains visible and accessible.
*
* @type {string}
* @memberOf BackButtonComponent
*/
@Input()
toolbarColor?: string;
/**
* @description Flag indicating if the icon is an Ionic icon.
* @summary This property is determined automatically based on the icon input.
* It's set to false for Tabler icons (prefixed with 'ti-') or SVG paths
* (containing '.svg'). This affects how the icon is rendered in the template.
*
* @type {boolean}
* @default true
* @memberOf BackButtonComponent
*/
isIonIcon: boolean = true;
/**
* @description Angular Location service.
* @summary Injected service that provides access to the browser's URL and history.
* This service is used for interacting with the browser's history API, allowing
* for back navigation and URL manipulation outside of Angular's router.
*
* @private
* @type {Location}
* @memberOf CrudFormComponent
*/
private location: Location = inject(Location);
/**
* @description Creates an instance of BackButtonComponent.
* @summary Initializes a new BackButtonComponent
*
* @memberOf BackButtonComponent
*/
constructor() {
addIcons({chevronBackOutline})
}
/**
* @description Initializes the component after Angular first displays the data-bound properties.
* @summary Sets up the component by processing boolean inputs, determining the button color based on
* toolbar color, retrieving the previous URL for navigation, and determining the icon type.
* This method prepares the component for user interaction by ensuring all properties are
* properly initialized.
*
* @mermaid
* sequenceDiagram
* participant A as Angular Lifecycle
* participant B as BackButtonComponent
* participant R as Location
*
* A->>B: ngOnInit()
* B->>B: Process preventDefault
* B->>B: Process emitEvent
* B->>B: Determine color based on toolbarColor
* B->>R: getPreviousUrl()
* R-->>B: Return previous URL
* B->>B: Store previousUrl
* B->>B: Process showText
* Note over B: Check icon type
* B->>B: Set isIonIcon based on icon string
*
* @memberOf BackButtonComponent
*/
ngOnInit(): void {
this.preventDefault = stringToBoolean(this.preventDefault);
this.emitEvent = stringToBoolean(this.emitEvent);
this.color = this.toolbarColor ? 'light' : 'primary';
this.showText = stringToBoolean(this.showText);
// if(this.showText)
// this.text = await this.localeService.get((!this.locale ? this.text : `${this.locale}.${this.text}`) as string);
}
/**
* @description Handles navigation when the back button is clicked.
* @summary This method is the core navigation handler for the back button. It supports three
* navigation patterns: 1) custom function execution, 2) navigation to a specific URL, and
* 3) default back navigation using browser history. It also handles event emission and
* can prevent navigation when configured to do so.
*
* @param {boolean} forceRefresh - Whether to force a page refresh after navigation
* @return {Promise<boolean|void>} A promise that resolves when navigation is complete
*
* @mermaid
* sequenceDiagram
* participant U as User
* participant B as BackButtonComponent
* participant E as Event System
*
* U->>B: Click back button
* B->>B: backToPage(forceRefresh)
* alt preventDefault is true
* B->>B: handleEndNavigation(forceRefresh)
* B->>E: Emit navigation event
* B-->>U: Return without navigation
* else preventDefault is false
* B->>B: handleEndNavigation(forceRefresh)
* B->>E: Emit navigation event
* alt link is not provided
* B->>R: backToLastPage()
* else link is a function
* B->>B: Execute link function
* else link is a URL
* B->>R: navigateTo(link, direction)
* end
* end
*
* @memberOf BackButtonComponent
*/
async backToPage(forceRefresh = false): Promise<boolean|void> {
if(this.preventDefault)
return this.handleEndNavigation(forceRefresh);
this.handleEndNavigation(forceRefresh);
if(!this.link)
return this.location.back();
if(this.link instanceof Function)
return await this.link();
}
/**
* @description Emits navigation events when navigation is complete.
* @summary This method handles the emission of navigation events when the emitEvent property
* is true. It uses the windowEventEmitter utility to broadcast a global event that other
* components can listen for. The event includes information about whether a refresh is required.
*
* @param {boolean} forceRefresh - Whether to force a page refresh
* @return {void}
*
* @mermaid
* sequenceDiagram
* participant B as BackButtonComponent
* participant E as Event System
*
* B->>B: handleEndNavigation(forceRefresh)
* alt emitEvent is true
* B->>E: windowEventEmitter(BACK_BUTTON_NAVIGATION, {refresh: forceRefresh})
* end
*
* @memberOf BackButtonComponent
*/
handleEndNavigation(forceRefresh: boolean): void {
if(this.emitEvent)
windowEventEmitter(EventConstants.BACK_BUTTON_NAVIGATION, {refresh: forceRefresh});
}
/**
* @description Listens for global back button navigation events.
* @summary This method sets up an event listener for the BackButtonForceNavigationEvent,
* allowing external components to trigger back navigation programmatically. When this
* event is received, the component will execute its backToPage method as if the button
* was clicked directly.
*
* @param {Event} event - The event object
* @return {Promise<boolean|void>} A promise that resolves when navigation is complete
*
* @mermaid
* sequenceDiagram
* participant E as External Component
* participant W as Window
* participant B as BackButtonComponent
*
* E->>W: Dispatch BackButtonForceNavigationEvent
* W->>B: handleModelPageEvent(event)
* B->>B: backToPage()
* Note over B: Execute navigation logic
*
* @memberOf BackButtonComponent
*/
@HostListener('window:BackButtonForceNavigationEvent', ['$event'])
handleModelPageEvent(): Promise<boolean|void> {
return this.backToPage();
}
}
Source