UNPKG

xng-breadcrumb

Version:

A declarative and reactive breadcrumb approach for Angular 6 and beyond https://www.npmjs.com/package/xng-breadcrumb

453 lines (445 loc) 29.9 kB
import * as i0 from '@angular/core'; import { Directive, Injectable, TemplateRef, Component, ViewEncapsulation, ContentChild, Input, NgModule } from '@angular/core'; import { filter, map } from 'rxjs/operators'; import * as i1 from '@angular/router'; import { GuardsCheckEnd, RouterModule } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; import * as i3 from '@angular/common'; import { CommonModule } from '@angular/common'; /** * This directive is used to customize the breadcrumb label behavior * *xngBreadcrumbItem directive can be used in the child element of xng-breadcrumb * Usage: refer to the demo - app.component.html */ class BreadcrumbItemDirective { } BreadcrumbItemDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); BreadcrumbItemDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.0", type: BreadcrumbItemDirective, selector: "[xngBreadcrumbItem]", ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbItemDirective, decorators: [{ type: Directive, args: [{ selector: '[xngBreadcrumbItem]', }] }] }); const PATH_PARAM = { PREFIX: ':', REGEX_IDENTIFIER: '/:[^/]+', REGEX_REPLACER: '/[^/]+', }; const ALIAS_PREFIX = '@'; const isNonEmpty = (obj) => { return obj && Object.keys(obj).length > 0; }; class BreadcrumbService { constructor(activatedRoute, router) { this.activatedRoute = activatedRoute; this.router = router; this.baseHref = '/'; /** * dynamicBreadcrumbStore holds information about dynamically updated breadcrumbs. * Breadcrumbs can be set from anywhere (component, service) in the app. * On every breadcrumb update check this store and use the info if available. */ this.dynamicBreadcrumbStore = []; /** * breadcrumbList for the current route * When breadcrumb info is changed dynamically, check if the currentBreadcrumbs is effected * If effected, update the change and emit a new stream */ this.currentBreadcrumbs = []; this.previousBreadcrumbs = []; /** * Breadcrumbs observable to be subscribed by BreadcrumbComponent * Emits on every route change OR dynamic update of breadcrumb */ this.breadcrumbs = new BehaviorSubject([]); this.breadcrumbs$ = this.breadcrumbs.asObservable(); this.detectRouteChanges(); } /** * Whenever route changes build breadcrumb list again */ detectRouteChanges() { // Special case where breadcrumb service & component instantiates after a route is navigated. // Ex: put breadcrumbs within *ngIf and this.router.events would be empty // This check is also required where { initialNavigation: 'enabledBlocking' } is applied to routes this.setupBreadcrumbs(this.activatedRoute.snapshot); this.router.events .pipe(filter((event) => event instanceof GuardsCheckEnd)) .subscribe((event) => { // activatedRoute doesn't carry data when shouldReuseRoute returns false // use the event data with GuardsCheckEnd as workaround // Check for shouldActivate in case where the authGuard returns false the breadcrumbs shouldn't be changed if (event.shouldActivate) { this.setupBreadcrumbs(event.state.root); } }); } setupBreadcrumbs(activatedRouteSnapshot) { this.previousBreadcrumbs = this.currentBreadcrumbs; // breadcrumb label for base OR root path. Usually, this can be set as 'Home' const rootBreadcrumb = this.getRootBreadcrumb(); this.currentBreadcrumbs = rootBreadcrumb ? [rootBreadcrumb] : []; this.prepareBreadcrumbList(activatedRouteSnapshot, this.baseHref); } getRootBreadcrumb() { var _a; const rootConfig = this.router.config.find((config) => config.path === ''); const rootBreadcrumb = this.extractObject((_a = rootConfig === null || rootConfig === void 0 ? void 0 : rootConfig.data) === null || _a === void 0 ? void 0 : _a.breadcrumb); const storeItem = this.getFromStore(rootBreadcrumb.alias, '/'); if (isNonEmpty(rootBreadcrumb) || isNonEmpty(storeItem)) { return Object.assign(Object.assign(Object.assign(Object.assign({}, storeItem), rootBreadcrumb), { routeLink: this.baseHref }), this.getQueryParamsFromPreviousList('/')); } } prepareBreadcrumbItem(activatedRouteSnapshot, routeLinkPrefix) { const { path, breadcrumb } = this.parseRouteData(activatedRouteSnapshot.routeConfig); const resolvedSegment = this.resolvePathSegment(path, activatedRouteSnapshot); const routeLink = `${routeLinkPrefix}${resolvedSegment}`; const storeItem = this.getFromStore(breadcrumb.alias, routeLink); const label = this.extractLabel((storeItem === null || storeItem === void 0 ? void 0 : storeItem.label) || (breadcrumb === null || breadcrumb === void 0 ? void 0 : breadcrumb.label), resolvedSegment); let isAutoGeneratedLabel = false; let autoGeneratedLabel = ''; if (!label) { isAutoGeneratedLabel = true; autoGeneratedLabel = resolvedSegment; } return Object.assign(Object.assign(Object.assign(Object.assign({}, storeItem), breadcrumb), { label: isAutoGeneratedLabel ? autoGeneratedLabel : label, routeLink, isAutoGeneratedLabel }), this.getQueryParamsFromPreviousList(routeLink)); } prepareBreadcrumbList(activatedRouteSnapshot, routeLinkPrefix) { var _a; if ((_a = activatedRouteSnapshot.routeConfig) === null || _a === void 0 ? void 0 : _a.path) { const breadcrumbItem = this.prepareBreadcrumbItem(activatedRouteSnapshot, routeLinkPrefix); this.currentBreadcrumbs.push(breadcrumbItem); if (activatedRouteSnapshot.firstChild) { return this.prepareBreadcrumbList(activatedRouteSnapshot.firstChild, breadcrumbItem.routeLink + '/'); } } else if (activatedRouteSnapshot.firstChild) { return this.prepareBreadcrumbList(activatedRouteSnapshot.firstChild, routeLinkPrefix); } const lastCrumb = this.currentBreadcrumbs[this.currentBreadcrumbs.length - 1]; this.setQueryParamsForActiveBreadcrumb(lastCrumb, activatedRouteSnapshot); // remove breadcrumb items that needs to be hidden const breadcrumbsToShow = this.currentBreadcrumbs.filter((item) => !item.skip); this.breadcrumbs.next(breadcrumbsToShow); } getFromStore(alias, routeLink) { return this.dynamicBreadcrumbStore.find((item) => { return ((alias && alias === item.alias) || (routeLink && routeLink === item.routeLink) || this.matchRegex(routeLink, item.routeRegex)); }); } /** * use exact match instead of regexp.test * for /mentor/[^/]+ we should match '/mentor/12' but not '/mentor/12/abc' */ matchRegex(routeLink, routeRegex) { const match = routeLink.match(new RegExp(routeRegex)); return (match === null || match === void 0 ? void 0 : match[0]) === routeLink; } /** * if the path segment has route params, read the param value from url * for each segment of route this gets called * * for mentor/:id/view - it gets called with mentor, :id, view 3 times */ resolvePathSegment(segment, activatedRouteSnapshot) { //quirk -segment can be defined as view/:id in route config in which case you need to make it view/<resolved-param> if (segment.includes(PATH_PARAM.PREFIX)) { Object.entries(activatedRouteSnapshot.params).forEach(([key, value]) => { segment = segment.replace(`:${key}`, `${value}`); }); } return segment; } /** * queryParams & fragments for previous breadcrumb path are copied over to new list */ getQueryParamsFromPreviousList(routeLink) { const { queryParams, fragment } = this.previousBreadcrumbs.find((item) => item.routeLink === routeLink) || {}; return { queryParams, fragment }; } /** * set current activated route query params to the last breadcrumb item */ setQueryParamsForActiveBreadcrumb(lastItem, activatedRouteSnapshot) { if (lastItem) { const { queryParams, fragment } = activatedRouteSnapshot; lastItem.queryParams = queryParams ? Object.assign({}, queryParams) : undefined; lastItem.fragment = fragment; } } /** * For a specific route, breadcrumb can be defined either on parent OR it's child(which has empty path) * When both are defined, child takes precedence * * Ex: Below we are setting breadcrumb on both parent and child. * So, child takes precedence and "Defined On Child" is displayed for the route 'home' * { path: 'home', loadChildren: () => import('./home/home.module').then((m) => m.HomeModule) , data: {breadcrumb: "Defined On Module"}} * AND * children: [ * { path: '', component: ShowUserComponent, data: {breadcrumb: "Defined On Child" } * ] */ parseRouteData(routeConfig) { const { path, data } = routeConfig; const breadcrumb = this.mergeWithBaseChildData(routeConfig, data === null || data === void 0 ? void 0 : data.breadcrumb); return { path, breadcrumb }; } /** * get empty children of a module or Component. Empty child is the one with path: '' * When parent and it's children (that has empty route path) define data merge them both with child taking precedence */ mergeWithBaseChildData(routeConfig, config) { var _a; if (!routeConfig) { return this.extractObject(config); } let baseChild; if (routeConfig.loadChildren) { // To handle a module with empty child route baseChild = routeConfig._loadedRoutes.find((route) => route.path === ''); } else if (routeConfig.children) { // To handle a component with empty child route baseChild = routeConfig.children.find((route) => route.path === ''); } const childConfig = (_a = baseChild === null || baseChild === void 0 ? void 0 : baseChild.data) === null || _a === void 0 ? void 0 : _a.breadcrumb; return childConfig ? this.mergeWithBaseChildData(baseChild, Object.assign(Object.assign({}, this.extractObject(config)), this.extractObject(childConfig))) : this.extractObject(config); } /** * Update breadcrumb dynamically * * key can be a path | alias * * 1) Using complete route path. route can be passed the same way you define angular routes * - path can be passed as 'exact path(routeLink)' or 'path with params(routeRegex)' * - update label Ex: set('/mentor', 'Mentor'), set('/mentor/:id', 'Mentor Details') * - change visibility Ex: set('/mentor/:id/edit', { skip: true }) * ------------------------------------------ OR ------------------------------------------ * 2) Using route alias (prefixed with '@'). alias should be unique for a route * - update label Ex: set('@mentor', 'Enabler') * - change visibility Ex: set('@mentorEdit', { skip: true }) * * * value can be string | BreadcrumbObject | BreadcrumbFunction */ set(key, breadcrumb) { const breadcrumbObject = this.extractObject(breadcrumb); let updateArgs; if (key.startsWith(ALIAS_PREFIX)) { updateArgs = ['alias', Object.assign(Object.assign({}, breadcrumbObject), { alias: key.slice(1) })]; } else if (key.includes(PATH_PARAM.PREFIX)) { updateArgs = [ 'routeRegex', Object.assign(Object.assign({}, breadcrumbObject), { routeRegex: this.buildRegex(key) }), ]; } else { updateArgs = [ 'routeLink', Object.assign(Object.assign({}, breadcrumbObject), { routeLink: this.ensureLeadingSlash(key) }), ]; } // For this route if previously a breadcrumb is not defined that sets isAutoGeneratedLabel: true // change it to false since this is user supplied value updateArgs[1].isAutoGeneratedLabel = false; this.updateStore(...updateArgs); this.updateCurrentBreadcrumbs(...updateArgs); } /** * Update the store to reuse for dynamic declarations * If the store already has this route definition update it, else add */ updateStore(key, breadcrumb) { const storeItemIndex = this.dynamicBreadcrumbStore.findIndex((item) => { return breadcrumb[key] === item[key]; }); if (storeItemIndex > -1) { this.dynamicBreadcrumbStore[storeItemIndex] = Object.assign(Object.assign({}, this.dynamicBreadcrumbStore[storeItemIndex]), breadcrumb); } else { this.dynamicBreadcrumbStore.push(Object.assign({}, breadcrumb)); } } /** * If breadcrumb is present in current breadcrumbs update it and emit new stream */ updateCurrentBreadcrumbs(key, breadcrumb) { const itemIndex = this.currentBreadcrumbs.findIndex((item) => { return key === 'routeRegex' ? this.matchRegex(item.routeLink, breadcrumb[key]) : breadcrumb[key] === item[key]; }); if (itemIndex > -1) { this.currentBreadcrumbs[itemIndex] = Object.assign(Object.assign({}, this.currentBreadcrumbs[itemIndex]), breadcrumb); const breadcrumbsToShow = this.currentBreadcrumbs.filter((item) => !item.skip); this.breadcrumbs.next([...breadcrumbsToShow]); } } /** * For a route with path param, we create regex dynamically from angular route syntax * '/mentor/:id' becomes '/mentor/[^/]', * breadcrumbService.set('/mentor/:id', 'Uday') should update 'Uday' as label for '/mentor/2' OR 'mentor/ada' */ buildRegex(path) { return this.ensureLeadingSlash(path).replace(new RegExp(PATH_PARAM.REGEX_IDENTIFIER, 'g'), PATH_PARAM.REGEX_REPLACER); } ensureLeadingSlash(path) { return path.startsWith('/') ? path : `/${path}`; } /** * In App's RouteConfig, breadcrumb can be defined as a string OR a function OR an object * * string: simple static breadcrumb label for a path * function: callback that gets invoked with resolved path param * object: additional data defined along with breadcrumb label that gets passed to *xngBreadcrumbItem directive */ extractLabel(config, resolvedParam) { const label = typeof config === 'object' ? config.label : config; if (typeof label === 'function') { return label(resolvedParam); } return label; } extractObject(config) { // don't include {label} if config is undefined. This is important since we merge the configs if (config && (typeof config === 'string' || typeof config === 'function')) { return { label: config }; } return config || {}; } } BreadcrumbService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbService, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable }); BreadcrumbService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i1.Router }]; } }); class BreadcrumbComponent { constructor(breadcrumbService, activateRoute) { this.breadcrumbService = breadcrumbService; this._separator = '/'; /** * If true, breadcrumb is auto generated even without any mapping label * Default label is same as route segment */ this.autoGenerate = true; /** * By default query params will be preserved with breadcrumbs */ this.preserveQueryParams = true; /** * By default query fragments will be preserved with breadcrumbs */ this.preserveFragment = true; /** * custom class provided by consumer to increase specificity * This will benefit to override styles that are conflicting */ this.class = ''; this.setupMessage = 'not set up yet'; this.someParameterValue = null; // breadcrumb inside ngIf works only this way activateRoute.params.subscribe((params) => { this.setupComponent(params['someParam']); }); } /** * separator between breadcrumbs, defaults to '/'. * User can customize separator either by passing a String or Template * * String --> Ex: <xng-breadcrumb separator="-"> </xng-breadcrumb> * * Template --> Ex: <xng-breadcrumb [separator]="separatorTemplate"> </xng-breadcrumb> * <ng-template #separatorTemplate><mat-icon>arrow_right</mat-icon></ng-template> */ set separator(value) { if (value instanceof TemplateRef) { this.separatorTemplate = value; this._separator = undefined; } else { this.separatorTemplate = undefined; this._separator = value || '/'; } } get separator() { return this._separator; } setupComponent(someParam) { this.setupMessage = 'set up at ' + new Date(); this.someParameterValue = someParam; } ngOnInit() { this.breadcrumbs$ = this.breadcrumbService.breadcrumbs$.pipe(map((breadcrumbs) => { return breadcrumbs .filter((breadcrumb) => { // Usually, breadcrumb list can contain a combination of auto generated and user specified labels // this filters autogenerated labels in case of "[autoGenerate]: false" if (this.autoGenerate) { return true; } return !breadcrumb.isAutoGeneratedLabel; }) .map((breadcrumb) => { // Do not mutate breadcrumb as its source of truth. // There can be scenarios where we can have multiple xng-breadcrumb instances in page const { routeInterceptor, routeLink } = breadcrumb; return Object.assign(Object.assign({}, breadcrumb), { routeLink: (routeInterceptor === null || routeInterceptor === void 0 ? void 0 : routeInterceptor(routeLink, breadcrumb)) || routeLink }); }); })); } } BreadcrumbComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbComponent, deps: [{ token: BreadcrumbService }, { token: i1.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component }); BreadcrumbComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: BreadcrumbComponent, selector: "xng-breadcrumb", inputs: { autoGenerate: "autoGenerate", preserveQueryParams: "preserveQueryParams", preserveFragment: "preserveFragment", class: "class", anchorTarget: "anchorTarget", separator: "separator" }, queries: [{ propertyName: "itemTemplate", first: true, predicate: BreadcrumbItemDirective, descendants: true, read: TemplateRef }], ngImport: i0, template: "<nav aria-label=\"breadcrumb\" class=\"xng-breadcrumb-root\" [ngClass]=\"class\">\n <ol class=\"xng-breadcrumb-list\">\n <ng-container\n *ngFor=\"\n let breadcrumb of breadcrumbs$ | async;\n last as isLast;\n first as isFirst;\n index as index;\n count as count\n \"\n >\n <li class=\"xng-breadcrumb-item\">\n <a\n *ngIf=\"!isLast\"\n class=\"xng-breadcrumb-link\"\n [ngClass]=\"{ 'xng-breadcrumb-link-disabled': breadcrumb.disable }\"\n [attr.aria-disabled]=\"breadcrumb.disable\"\n [attr.tabIndex]=\"breadcrumb.disable ? -1 : 0\"\n role=\"button\"\n rel=\"noopener noreferrer\"\n [routerLink]=\"\n breadcrumb.routeInterceptor\n ? breadcrumb.routeInterceptor(breadcrumb.routeLink, breadcrumb)\n : breadcrumb.routeLink\n \"\n [queryParams]=\"\n preserveQueryParams ? breadcrumb.queryParams : undefined\n \"\n [fragment]=\"preserveFragment ? breadcrumb.fragment : undefined\"\n [target]=\"anchorTarget ? anchorTarget : '_self'\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate;\n context: {\n $implicit: breadcrumb.label,\n info: breadcrumb.info,\n last: isLast,\n first: isFirst,\n index: index,\n count: count\n }\n \"\n ></ng-container>\n <ng-container *ngIf=\"!itemTemplate\">{{\n breadcrumb.label\n }}</ng-container>\n </a>\n\n <label *ngIf=\"isLast\" class=\"xng-breadcrumb-trail\">\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate;\n context: {\n $implicit: breadcrumb.label,\n info: breadcrumb.info,\n last: isLast,\n first: isFirst,\n index: index,\n count: count\n }\n \"\n ></ng-container>\n <ng-container *ngIf=\"!itemTemplate\">{{\n breadcrumb.label\n }}</ng-container>\n </label>\n </li>\n\n <li *ngIf=\"!isLast\" class=\"xng-breadcrumb-separator\" aria-hidden=\"true\">\n <ng-container *ngTemplateOutlet=\"separatorTemplate\"></ng-container>\n <ng-container *ngIf=\"!separatorTemplate\">{{ separator }}</ng-container>\n </li>\n </ng-container>\n </ol>\n</nav>\n", styles: [".xng-breadcrumb-root{margin:0;color:#0009}.xng-breadcrumb-list{display:flex;align-items:center;flex-wrap:wrap;margin:0;padding:0}.xng-breadcrumb-item{list-style:none}.xng-breadcrumb-trail{display:flex;align-items:center;color:#000000e6}.xng-breadcrumb-link{display:flex;align-items:center;white-space:nowrap;color:inherit;text-decoration:none;transition:-webkit-text-decoration .3s;transition:text-decoration .3s;transition:text-decoration .3s,-webkit-text-decoration .3s;cursor:pointer}.xng-breadcrumb-link:hover{text-decoration:underline}.xng-breadcrumb-link-disabled{pointer-events:none;cursor:disabled}.xng-breadcrumb-separator{display:flex;-webkit-user-select:none;user-select:none;margin-left:8px;margin-right:8px}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }], encapsulation: i0.ViewEncapsulation.None }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbComponent, decorators: [{ type: Component, args: [{ selector: 'xng-breadcrumb', encapsulation: ViewEncapsulation.None, template: "<nav aria-label=\"breadcrumb\" class=\"xng-breadcrumb-root\" [ngClass]=\"class\">\n <ol class=\"xng-breadcrumb-list\">\n <ng-container\n *ngFor=\"\n let breadcrumb of breadcrumbs$ | async;\n last as isLast;\n first as isFirst;\n index as index;\n count as count\n \"\n >\n <li class=\"xng-breadcrumb-item\">\n <a\n *ngIf=\"!isLast\"\n class=\"xng-breadcrumb-link\"\n [ngClass]=\"{ 'xng-breadcrumb-link-disabled': breadcrumb.disable }\"\n [attr.aria-disabled]=\"breadcrumb.disable\"\n [attr.tabIndex]=\"breadcrumb.disable ? -1 : 0\"\n role=\"button\"\n rel=\"noopener noreferrer\"\n [routerLink]=\"\n breadcrumb.routeInterceptor\n ? breadcrumb.routeInterceptor(breadcrumb.routeLink, breadcrumb)\n : breadcrumb.routeLink\n \"\n [queryParams]=\"\n preserveQueryParams ? breadcrumb.queryParams : undefined\n \"\n [fragment]=\"preserveFragment ? breadcrumb.fragment : undefined\"\n [target]=\"anchorTarget ? anchorTarget : '_self'\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate;\n context: {\n $implicit: breadcrumb.label,\n info: breadcrumb.info,\n last: isLast,\n first: isFirst,\n index: index,\n count: count\n }\n \"\n ></ng-container>\n <ng-container *ngIf=\"!itemTemplate\">{{\n breadcrumb.label\n }}</ng-container>\n </a>\n\n <label *ngIf=\"isLast\" class=\"xng-breadcrumb-trail\">\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate;\n context: {\n $implicit: breadcrumb.label,\n info: breadcrumb.info,\n last: isLast,\n first: isFirst,\n index: index,\n count: count\n }\n \"\n ></ng-container>\n <ng-container *ngIf=\"!itemTemplate\">{{\n breadcrumb.label\n }}</ng-container>\n </label>\n </li>\n\n <li *ngIf=\"!isLast\" class=\"xng-breadcrumb-separator\" aria-hidden=\"true\">\n <ng-container *ngTemplateOutlet=\"separatorTemplate\"></ng-container>\n <ng-container *ngIf=\"!separatorTemplate\">{{ separator }}</ng-container>\n </li>\n </ng-container>\n </ol>\n</nav>\n", styles: [".xng-breadcrumb-root{margin:0;color:#0009}.xng-breadcrumb-list{display:flex;align-items:center;flex-wrap:wrap;margin:0;padding:0}.xng-breadcrumb-item{list-style:none}.xng-breadcrumb-trail{display:flex;align-items:center;color:#000000e6}.xng-breadcrumb-link{display:flex;align-items:center;white-space:nowrap;color:inherit;text-decoration:none;transition:-webkit-text-decoration .3s;transition:text-decoration .3s;transition:text-decoration .3s,-webkit-text-decoration .3s;cursor:pointer}.xng-breadcrumb-link:hover{text-decoration:underline}.xng-breadcrumb-link-disabled{pointer-events:none;cursor:disabled}.xng-breadcrumb-separator{display:flex;-webkit-user-select:none;user-select:none;margin-left:8px;margin-right:8px}\n"] }] }], ctorParameters: function () { return [{ type: BreadcrumbService }, { type: i1.ActivatedRoute }]; }, propDecorators: { itemTemplate: [{ type: ContentChild, args: [BreadcrumbItemDirective, { static: false, read: TemplateRef }] }], autoGenerate: [{ type: Input }], preserveQueryParams: [{ type: Input }], preserveFragment: [{ type: Input }], class: [{ type: Input }], anchorTarget: [{ type: Input }], separator: [{ type: Input, args: ['separator'] }] } }); class BreadcrumbModule { } BreadcrumbModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); BreadcrumbModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbModule, declarations: [BreadcrumbComponent, BreadcrumbItemDirective], imports: [CommonModule, RouterModule], exports: [BreadcrumbComponent, BreadcrumbItemDirective] }); BreadcrumbModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbModule, imports: [CommonModule, RouterModule] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: BreadcrumbModule, decorators: [{ type: NgModule, args: [{ declarations: [BreadcrumbComponent, BreadcrumbItemDirective], imports: [CommonModule, RouterModule], exports: [BreadcrumbComponent, BreadcrumbItemDirective], }] }] }); /* * Public API Surface of xng-breadcrumb */ /** * Generated bundle index. Do not edit. */ export { BreadcrumbComponent, BreadcrumbItemDirective, BreadcrumbModule, BreadcrumbService }; //# sourceMappingURL=xng-breadcrumb.mjs.map