Skip to content

Commit

Permalink
[angular-xmcloud] Introduce angular SXA navigation component (#1894)
Browse files Browse the repository at this point in the history
* sxa navigation implementation

* fix shared module

* fix image component bug; rename variable in navigation item component

* fix typo

* update changelog

* remove console logs
  • Loading branch information
yavorsk authored Aug 23, 2024
1 parent dca02e2 commit 2843aa2
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Our versioning strategy is as follows:
* Column-Splitter ([#1889](https://github.com/Sitecore/jss/pull/1889))
* PartialDesignDynamicPlaceholder ([#1902](https://github.com/Sitecore/jss/pull/1902))
* Promo component ([#1897](https://github.com/Sitecore/jss/pull/1897))
* Navigation ([#1894](https://github.com/Sitecore/jss/pull/1894))

### 🛠 Breaking Change

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { RouterModule } from '@angular/router';
import { JssModule } from '@sitecore-jss/sitecore-jss-angular';
import { NavigationItemComponent } from './navigation/navigation-item.component';

/*
This module is imported by the generated app-components.module.ts.
You can use this module to provide shared Angular components that are not
JSS components, etc to the generated module.
Don't want code generation? See ./.gitignore for instructions to turn it off.
*/
@NgModule({
imports: [CommonModule, TranslateModule, RouterModule, JssModule, FormsModule],
exports: [CommonModule, TranslateModule, RouterModule, FormsModule, NavigationItemComponent],
declarations: [NavigationItemComponent],
})
export class AppComponentsSharedModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<ng-template #default>
<div class="component image {{ styles }}" [attr.id]="id" *ngIf="rendering.fields; else empty">
<div className="component-content">
<div class="component-content">
<ng-container *ngIf="isEditing || !rendering.fields.TargetUrl?.value?.href; else withLink">
<img *scImage="rendering.fields.Image" />
</ng-container>
Expand All @@ -28,7 +28,7 @@
</ng-template>

<ng-template #empty>
<div className="component image {{ styles }}">
<div class="component image {{ styles }}">
<div class="component-content">
<span class="is-empty-hint">Image</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<li [class]="cssClasses" [ngClass]="{ active: isActive }" tabIndex="0">
<div [ngClass]="{ 'navigation-title': true, child: hasChildren }" (click)="isActive = !isActive">
<a *scLink="linkField" (click)="onClick($event)">
<ng-container *ngIf="navItemFields.NavigationTitle"
><span *scText="navItemFields.NavigationTitle"></span
></ng-container>
<ng-container *ngIf="!navItemFields.NavigationTitle && navItemFields.Title">
<span *scText="navItemFields.Title"></span
></ng-container>
<ng-container *ngIf="!navItemFields.NavigationTitle && !navItemFields.Title">{{
navItemFields.DisplayName
}}</ng-container>
</a>
</div>
<ul *ngIf="hasChildren" class="clearfix">
<app-navigation-item
*ngFor="let childNavItemFields of navItemFields.Children"
[navItemFields]="childNavItemFields"
[relativeLevel]="childrenRelativeLevel"
(childLinkClickEvent)="onClick($event)"
></app-navigation-item>
</ul>
</li>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
import { LinkField } from '@sitecore-jss/sitecore-jss-angular';
import { Field } from '@sitecore-jss/sitecore-jss-angular';

export interface NavItemFields {
Id: string;
DisplayName: string;
Title: Field<string>;
NavigationTitle: Field<string>;
Href: string;
Querystring: string;
Children: Array<NavItemFields>;
Styles: string[];
}

@Component({
selector: 'app-navigation-item',
templateUrl: './navigation-item.component.html',
})
export class NavigationItemComponent implements OnInit {
@Input() navItemFields: NavItemFields;
@Input() relativeLevel: number;
@Output() childLinkClickEvent: EventEmitter<Event> = new EventEmitter<Event>();
cssClasses = '';
isActive = false;
linkField = {};
childrenRelativeLevel = 0;
hasChildren = false;

constructor() {}

ngOnInit() {
this.cssClasses = `${this.navItemFields.Styles.concat('rel-level' + this.relativeLevel).join(
' '
)}`;
this.linkField = this.getLinkField(this.navItemFields);
this.hasChildren = this.navItemFields.Children && this.navItemFields.Children.length != 0;
this.childrenRelativeLevel = this.relativeLevel + 1;
}

onClick(event: Event) {
this.childLinkClickEvent.emit(event);
}

private getLinkField = (navItemFields: NavItemFields): LinkField => ({
value: {
href: navItemFields.Href,
title: this.getLinkTitle(navItemFields),
querystring: navItemFields.Querystring,
},
});

private getLinkTitle = (navItemFields: NavItemFields): string | undefined => {
let title;
if (navItemFields.NavigationTitle?.value) {
title = navItemFields.NavigationTitle.value.toString();
} else if (navItemFields.Title?.value) {
title = navItemFields.Title.value.toString();
} else {
title = navItemFields.DisplayName;
}

return title;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div
class="component navigation {{ styles }} {{ this.rendering.params?.GridParameters }}"
[attr.id]="id"
>
<label class="menu-mobile-navigate-wrapper">
<input
type="checkbox"
class="menu-mobile-navigate"
[checked]="isOpenMenu"
(change)="toggleMenu($event)"
/>
<div class="menu-humburger"></div>
<div class="component-content">
<nav>
<ul class="clearfix">
<app-navigation-item
*ngFor="let navItemFields of rendering.fields"
[navItemFields]="navItemFields"
[relativeLevel]="baseLevel"
(childLinkClickEvent)="toggleMenu($event, false)"
></app-navigation-item>
</ul>
</nav>
</div>
</label>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { SxaComponent } from '../sxa.component';
import { JssContextService } from '../../jss-context.service';

@Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
})
export class NavigationComponent extends SxaComponent implements OnInit, OnDestroy {
isEditing = false;
private contextSubscription: Subscription;
isOpenMenu = false;
baseLevel = 1;

constructor(private jssContext: JssContextService) {
super();
}

ngOnInit() {
super.ngOnInit();
this.contextSubscription = this.jssContext.state.subscribe((newState) => {
this.isEditing = newState.sitecore && newState.sitecore.context.pageEditing;
});
}

ngOnDestroy() {
if (this.contextSubscription) {
this.contextSubscription.unsubscribe();
}
}

toggleMenu(event: Event, flag?: boolean) {
if (event && this.isEditing) {
event.preventDefault();
}

if (flag !== undefined) {
this.isOpenMenu = flag;
}

this.isOpenMenu = !this.isOpenMenu;
}
}

0 comments on commit 2843aa2

Please sign in to comment.