Skip to content

Commit

Permalink
feat: customization support for breadcrumb
Browse files Browse the repository at this point in the history
- label with *xngBreadcrumbItem directive.
- custom icon/ component for separator using ng-template
- better API with single entry point method i.e set()
- better styling and class names to support custom components
- deprecated methods and properties with warning messages
  • Loading branch information
udayvunnam committed Nov 3, 2019
1 parent c8ee93a commit b9aff64
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 153 deletions.
17 changes: 0 additions & 17 deletions projects/xng-breadcrumb/src/lib/breadcrumb-data.ts

This file was deleted.

8 changes: 8 additions & 0 deletions projects/xng-breadcrumb/src/lib/breadcrumb-item.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Directive } from '@angular/core';

@Directive({
selector: '[xngBreadcrumbItem]'
})
export class BreadcrumbItemDirective {
constructor() {}
}
31 changes: 21 additions & 10 deletions projects/xng-breadcrumb/src/lib/breadcrumb.component.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<ng-container *ngFor="let breadcrumb of breadcrumbs$ | async; last as isLast">
<li class="breadcrumb-item" [ngClass]="{ active: isLast }" [attr.aria-current]="isLast ? 'page' : null" *ngIf="!breadcrumb.skip">
<a *ngIf="!isLast; else lastRoute" [routerLink]="[breadcrumb.routeLink]" routerLinkActive="active">
{{ breadcrumb.label }}
<nav aria-label="breadcrumb" class="xng-breadcrumb-root">
<ol class="xng-breadcrumb-list">
<ng-container *ngFor="let breadcrumb of breadcrumbs$ | async; last as isLast; first as isFirst">
<li class="xng-breadcrumb-item">
<a *ngIf="!isLast" [routerLink]="[breadcrumb.routeLink]" class="xng-breadcrumb-link">
<ng-container
*ngTemplateOutlet="itemTemplate; context: { $implicit: breadcrumb.label, info: breadcrumb.info, last: isLast, first: isFirst }"
></ng-container>
<ng-container *ngIf="!itemTemplate">{{ breadcrumb.label }}</ng-container>
</a>
<ng-template #lastRoute>
<label class="current-path">{{ breadcrumb.label }}</label>
</ng-template>
<span class="separator" aria-hidden="true" *ngIf="!isLast"> {{ separator }}</span>

<label *ngIf="isLast" class="xng-breadcrumb-trail">
<ng-container
*ngTemplateOutlet="itemTemplate; context: { $implicit: breadcrumb.label, info: breadcrumb.info, last: isLast, first: isFirst }"
></ng-container>
<ng-container *ngIf="!itemTemplate">{{ breadcrumb.label }}</ng-container>
</label>
</li>

<li class="xng-breadcrumb-separator" aria-hidden="true" role="separator" *ngIf="!isLast">
<ng-container *ngTemplateOutlet="separatorTemplate"></ng-container>
<ng-container *ngIf="!separatorTemplate">{{ separator }}</ng-container>
</li>
</ng-container>
</ol>
Expand Down
61 changes: 27 additions & 34 deletions projects/xng-breadcrumb/src/lib/breadcrumb.component.scss
Original file line number Diff line number Diff line change
@@ -1,49 +1,42 @@
.breadcrumb {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
.xng-breadcrumb-root {
margin: 0;
color: rgba(0, 0, 0, 0.6);
}

ol {
.xng-breadcrumb-list {
display: flex;
align-items: center;
flex-wrap: wrap;
margin: 0;
padding: 0;
list-style: none;
}

a {
cursor: pointer;
box-sizing: border-box;
opacity: 0.75;
white-space: nowrap;
color: inherit;
background: transparent;
line-height: 36px;
padding: 0 8px;
outline: 0;
text-decoration: none;
.xng-breadcrumb-item {
list-style: none;
}

a:hover,
a:focus {
text-decoration: unset;
opacity: 1;
.xng-breadcrumb-trail {
display: flex;
align-items: center;
color: rgba(0, 0, 0, 0.9);
}

.current-path {
box-sizing: border-box;
.xng-breadcrumb-link {
display: flex;
align-items: center;
white-space: nowrap;
color: inherit;
background: transparent;
line-height: 36px;
padding: 0 8px;
opacity: 0.75;
text-decoration: none;
transition: text-decoration 0.3s;
}

.xng-breadcrumb-link:hover {
text-decoration: underline;
}

.separator {
display: inline-block;
opacity: 0.5;
font-weight: normal;
font-style: normal;
font-size: 1.1em;
.xng-breadcrumb-separator {
display: flex;
user-select: none;
margin-left: 8px;
margin-right: 8px;
}
46 changes: 38 additions & 8 deletions projects/xng-breadcrumb/src/lib/breadcrumb.component.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,57 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, ContentChild, Input, OnInit, TemplateRef } from '@angular/core';
import { Observable } from 'rxjs';
import { BreadcrumbItemDirective } from './breadcrumb-item.directive';
import { BreadcrumbService } from './breadcrumb.service';
import { Breadcrumb } from './breadcrumb';

@Component({
selector: 'xng-breadcrumb',
templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.scss']
})
export class BreadcrumbComponent implements OnInit {
breadcrumbs$: Observable<any[]>;
breadcrumbs$: Observable<Breadcrumb[]>;
separatorTemplate: TemplateRef<void>;
private _separator = '/';

/**
* If true, breacrumb is formed even without any configuration
* Default mapping is same as route path with intial letter capitalized
*
* Breadcrumb item can be customized with this template
* Template context is provided label, additional info, first and last indexes
* Use cases:
* 1) Add an icon along with label
* 2) i18n. {{breadcrumb | translate}} or {{breadcrumb | transloco}}
* 3) Change text case {{breadcrumb | titlecase}}
*/
@ContentChild(BreadcrumbItemDirective, { static: false, read: TemplateRef }) itemTemplate;

/**
* If true, breacrumb is auto generated even without any mapping label
* Default label is same as route segment
*/
@Input() defaultMapping = true;

/**
* separator between breadcrumbs
* defaults to '/'. Other options could be '.' or '-' or '>' etc.
* 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>
*/
@Input() separator = '/';
@Input('separator')
set separator(value: string | TemplateRef<void>) {
if (value instanceof TemplateRef) {
this.separatorTemplate = value;
this._separator = undefined;
} else {
this.separatorTemplate = undefined;
this._separator = value || '/';
}
}
get separator() {
return this._separator;
}

constructor(private breadcrumbService: BreadcrumbService) {}

Expand Down
5 changes: 3 additions & 2 deletions projects/xng-breadcrumb/src/lib/breadcrumb.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BreadcrumbItemDirective } from './breadcrumb-item.directive';
import { BreadcrumbComponent } from './breadcrumb.component';

@NgModule({
declarations: [BreadcrumbComponent],
declarations: [BreadcrumbComponent, BreadcrumbItemDirective],
imports: [CommonModule, RouterModule],
exports: [BreadcrumbComponent]
exports: [BreadcrumbComponent, BreadcrumbItemDirective]
})
export class BreadcrumbModule {}
Loading

0 comments on commit b9aff64

Please sign in to comment.