Skip to content

Commit

Permalink
feat(context-menu): add dynamic inputs (#1221)
Browse files Browse the repository at this point in the history
Closes #1101, closes #1073
  • Loading branch information
nnixaa authored Feb 11, 2019
1 parent 6c9e11c commit 9f8d659
Show file tree
Hide file tree
Showing 4 changed files with 423 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { Component, Input } from '@angular/core';

import { NbMenuItem } from '../../components/menu/menu.service';
import { NbPositionedContainer } from '../cdk';
import { NbPositionedContainer, NbRenderableContainer } from '../cdk';

/**
* Context menu component used as content within NbContextMenuDirective.
Expand All @@ -23,10 +23,21 @@ import { NbPositionedContainer } from '../cdk';
styleUrls: ['./context-menu.component.scss'],
template: `
<span class="arrow"></span>
<nb-menu class="context-menu" [items]="items" [tag]="tag"></nb-menu>
<nb-menu class="context-menu" [items]="context.items" [tag]="context.tag"></nb-menu>
`,
})
export class NbContextMenuComponent extends NbPositionedContainer {
export class NbContextMenuComponent extends NbPositionedContainer implements NbRenderableContainer {

@Input() items: NbMenuItem[] = [];
@Input() tag: string;

@Input()
context: { items: NbMenuItem[], tag?: string } = { items: [] };


/**
* The method is empty since we don't need to do anything additionally
* render is handled by change detection
*/
renderContent() {}
}
125 changes: 45 additions & 80 deletions src/framework/theme/components/context-menu/context-menu.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,28 @@

import {
AfterViewInit,
ComponentFactoryResolver,
ComponentRef,
Directive,
ElementRef,
Inject,
Input,
OnChanges,
OnDestroy,
OnInit,
} from '@angular/core';
import { filter, takeWhile } from 'rxjs/operators';

import {
createContainer,
NbAdjustableConnectedPositionStrategy,
NbAdjustment,
NbDynamicOverlay,
NbDynamicOverlayController,
NbDynamicOverlayHandler,
NbOverlayRef,
NbOverlayService,
NbPosition,
NbPositionBuilderService,
NbTrigger,
NbTriggerStrategy,
NbTriggerStrategyBuilderService,
patch,
} from '../cdk';
import { NbContextMenuComponent } from './context-menu.component';
import { NbMenuItem, NbMenuService } from '../menu/menu.service';
import { NB_DOCUMENT } from '../../theme.options';

/**
* Full featured context menu directive.
Expand Down Expand Up @@ -106,8 +102,11 @@ import { NB_DOCUMENT } from '../../theme.options';
*
* @stacked-example(Manual Control, context-menu/context-menu-noop.component)
* */
@Directive({ selector: '[nbContextMenu]' })
export class NbContextMenuDirective implements AfterViewInit, OnDestroy {
@Directive({
selector: '[nbContextMenu]',
providers: [NbDynamicOverlayHandler, NbDynamicOverlay],
})
export class NbContextMenuDirective implements NbDynamicOverlayController, OnChanges, AfterViewInit, OnDestroy, OnInit {

/**
* Position will be calculated relatively host element based on the position.
Expand All @@ -134,9 +133,9 @@ export class NbContextMenuDirective implements AfterViewInit, OnDestroy {
* Basic menu items, will be passed to the internal NbMenuComponent.
* */
@Input('nbContextMenu')
set setItems(items: NbMenuItem[]) {
set items(items: NbMenuItem[]) {
this.validateItems(items);
this.items = items;
this._items = items;
};

/**
Expand All @@ -150,96 +149,62 @@ export class NbContextMenuDirective implements AfterViewInit, OnDestroy {
protected container: ComponentRef<any>;
protected positionStrategy: NbAdjustableConnectedPositionStrategy;
protected alive: boolean = true;
private items: NbMenuItem[] = [];
private _items: NbMenuItem[] = [];

constructor(@Inject(NB_DOCUMENT) protected document,
private dynamicOverlay: NbDynamicOverlay;

constructor(private hostRef: ElementRef,
private menuService: NbMenuService,
private hostRef: ElementRef,
private positionBuilder: NbPositionBuilderService,
private triggerStrategyBuilder: NbTriggerStrategyBuilderService,
private overlay: NbOverlayService,
private componentFactoryResolver: ComponentFactoryResolver) {
private dynamicOverlayHandler: NbDynamicOverlayHandler) {
}

ngOnInit() {
this.dynamicOverlayHandler
.host(this.hostRef)
.componentType(NbContextMenuComponent);
}

ngOnChanges() {
this.rebuild();
}

ngAfterViewInit() {
this.subscribeOnTriggers();
this.dynamicOverlay = this.configureDynamicOverlay()
.build();
this.subscribeOnItemClick();
this.subscribeOnPositionChange();
}

ngOnDestroy() {
this.alive = false;
this.hide();
if (this.ref) {
this.ref.dispose();
}
rebuild() {
this.dynamicOverlay = this.configureDynamicOverlay()
.rebuild();
}

show() {
if (!this.ref) {
this.createOverlay();
}

this.openContextMenu();
this.dynamicOverlay.show();
}

hide() {
if (this.ref) {
this.ref.detach();
}

this.container = null;
this.dynamicOverlay.hide();
}

toggle() {
if (this.ref && this.ref.hasAttached()) {
this.hide();
} else {
this.show();
}
}

protected createOverlay() {
this.ref = this.overlay.create({
positionStrategy: this.positionStrategy,
scrollStrategy: this.overlay.scrollStrategies.reposition(),
});
this.dynamicOverlay.toggle();
}

protected openContextMenu() {
this.container = createContainer(this.ref, NbContextMenuComponent, {
position: this.position,
items: this.items,
tag: this.tag,
}, this.componentFactoryResolver);
ngOnDestroy() {
this.dynamicOverlayHandler.destroy();
}

protected createPositionStrategy(): NbAdjustableConnectedPositionStrategy {
return this.positionBuilder
.connectedTo(this.hostRef)
protected configureDynamicOverlay() {
return this.dynamicOverlayHandler
.position(this.position)
.adjustment(this.adjustment);
}

protected createTriggerStrategy(): NbTriggerStrategy {
return this.triggerStrategyBuilder
.trigger(this.trigger)
.host(this.hostRef.nativeElement)
.container(() => this.container)
.build();
}

protected subscribeOnPositionChange() {
this.positionStrategy = this.createPositionStrategy();
this.positionStrategy.positionChange
.pipe(takeWhile(() => this.alive))
.subscribe((position: NbPosition) => patch(this.container, { position }));
}

protected subscribeOnTriggers() {
const triggerStrategy = this.createTriggerStrategy();
triggerStrategy.show$.pipe(takeWhile(() => this.alive)).subscribe(() => this.show());
triggerStrategy.hide$.pipe(takeWhile(() => this.alive)).subscribe(() => this.hide());
.adjustment(this.adjustment)
.context({
position: this.position,
items: this._items,
tag: this.tag,
});
}

/*
Expand Down
Loading

0 comments on commit 9f8d659

Please sign in to comment.