Skip to content

Commit

Permalink
feat(tooltip): dynamic inputs support (#1184)
Browse files Browse the repository at this point in the history
Closes #1052 #1159
  • Loading branch information
nnixaa committed Jan 30, 2019
1 parent c8cf626 commit 9ce7019
Show file tree
Hide file tree
Showing 6 changed files with 477 additions and 290 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export class MockPositionBuilder {
_connectedTo: ElementRef<any>;
_position: NbPosition;
_adjustment: NbAdjustment;
_offset: number;

connectedTo(connectedTo: ElementRef<any>) {
this._connectedTo = connectedTo;
Expand All @@ -117,6 +118,11 @@ export class MockPositionBuilder {
return this;
}

offset(offset) {
this._offset = offset;
return this;
}

attach() {
};

Expand Down Expand Up @@ -400,7 +406,7 @@ describe('dynamic-overlay-handler', () => {
expect(positionBuilder._connectedTo).toBe(host1);
expect(positionBuilder._position).toBe(NbPosition.TOP);
expect(positionBuilder._adjustment).toBe(NbAdjustment.NOOP);

expect(positionBuilder._offset).toBe(0);

configure().host(host2).rebuild();
expect(triggerStrategyBuilder._host).toBe(host2.nativeElement);
Expand All @@ -409,6 +415,7 @@ describe('dynamic-overlay-handler', () => {
expect(positionBuilder._connectedTo).toBe(host2);
expect(positionBuilder._position).toBe(NbPosition.TOP);
expect(positionBuilder._adjustment).toBe(NbAdjustment.NOOP);
expect(positionBuilder._offset).toBe(0);
});

it('should set and update position', () => {
Expand Down Expand Up @@ -437,6 +444,21 @@ describe('dynamic-overlay-handler', () => {
expect(positionBuilder._adjustment).toBe(NbAdjustment.COUNTERCLOCKWISE);
});

it('should set and update offset', () => {
const host = new ElementRef(document.createElement('b'));
configure(host).offset(34).build();
expect(positionBuilder._connectedTo).toBe(host);
expect(positionBuilder._position).toBe(NbPosition.TOP);
expect(positionBuilder._adjustment).toBe(NbAdjustment.NOOP);
expect(positionBuilder._offset).toBe(34);

configure(host).offset(2).rebuild();
expect(positionBuilder._connectedTo).toBe(host);
expect(positionBuilder._position).toBe(NbPosition.TOP);
expect(positionBuilder._adjustment).toBe(NbAdjustment.NOOP);
expect(positionBuilder._offset).toBe(2);
});

it('should set and update content', () => {
const content1 = 'new content';
const content2 = 'new new content';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class NbDynamicOverlayHandler {
protected _trigger: NbTrigger = NbTrigger.NOOP;
protected _position: NbPosition = NbPosition.TOP;
protected _adjustment: NbAdjustment = NbAdjustment.NOOP;
protected _offset: number = 0;

protected dynamicOverlay: NbDynamicOverlay;

Expand Down Expand Up @@ -89,6 +90,12 @@ export class NbDynamicOverlayHandler {
return this;
}

offset(offset: number) {
this.changes.offset = new NbDynamicOverlayChange(this._offset, offset);
this._offset = offset;
return this;
}

build() {
if (!this._componentType || !this._host) {
throw Error(`NbDynamicOverlayHandler: at least 'componentType' and 'host' should be
Expand Down Expand Up @@ -163,7 +170,8 @@ export class NbDynamicOverlayHandler {
return this.positionBuilder
.connectedTo(this._host)
.position(this._position)
.adjustment(this._adjustment);
.adjustment(this._adjustment)
.offset(this._offset);
}

protected subscribeOnTriggers(dynamicOverlay: NbDynamicOverlay) {
Expand Down Expand Up @@ -192,7 +200,7 @@ export class NbDynamicOverlayHandler {
}

protected isPositionStrategyUpdateRequired(): boolean {
return this.isAdjustmentUpdated() || this.isPositionUpdated() || this.isHostUpdated();
return this.isAdjustmentUpdated() || this.isPositionUpdated() || this.isOffsetUpdated() || this.isHostUpdated();
}

protected isTriggerStrategyUpdateRequired(): boolean {
Expand Down Expand Up @@ -231,6 +239,10 @@ export class NbDynamicOverlayHandler {
return this.changes.trigger && this.changes.trigger.isChanged();
}

protected isOffsetUpdated(): boolean {
return this.changes.offset && this.changes.offset.isChanged();
}

protected clearChanges() {
this.changes = {};
}
Expand Down
25 changes: 4 additions & 21 deletions src/framework/theme/components/popover/popover.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { Component, ElementRef, Input, NgModule, TemplateRef, Type, ViewChild }
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';


import { NbThemeModule } from '../../theme.module';
import { NbLayoutModule } from '../layout/layout.module';
import { NbPopoverDirective } from './popover.directive';
import {
NbAdjustment,
NbDynamicOverlayHandler,
Expand All @@ -14,8 +12,9 @@ import {
NbRenderableContainer,
NbTrigger,
} from '../cdk';
import { NbPopoverComponent } from '@nebular/theme/components/popover/popover.component';
import { NbPopoverModule } from '@nebular/theme';
import { NbPopoverDirective } from './popover.directive';
import { NbPopoverComponent } from './popover.component';
import { NbPopoverModule } from './popover.module';

@Component({
selector: 'nb-popover-component-content-test',
Expand Down Expand Up @@ -73,7 +72,7 @@ export class NbPopoverBindingsTestComponent {
template: `
<nb-layout>
<nb-layout-column>
<button #button [nbPopover]="test"></button>
<button #button nbPopover="test"></button>
</nb-layout-column>
</nb-layout>
Expand All @@ -86,22 +85,6 @@ export class NbPopoverInstanceTestComponent {
@ViewChild(TemplateRef) template: TemplateRef<any>;
}

@Component({
selector: 'nb-popover-mode-test',
template: `
<nb-layout>
<nb-layout-column>
<button #button nbPopover="test" [nbPopoverMode]="mode"></button>
</nb-layout-column>
</nb-layout>
`,
})
export class NbPopoverModeTestComponent {
@Input() mode: NbTrigger = NbTrigger.CLICK;
@ViewChild('button') button: ElementRef;
@ViewChild(NbPopoverDirective) popover: NbPopoverDirective;
}

const dynamicOverlay = {
show() {},
hide() {},
Expand Down
10 changes: 8 additions & 2 deletions src/framework/theme/components/tooltip/tooltip.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { Component, HostBinding, Input } from '@angular/core';

import { NbPosition } from '../cdk';
import { NbPosition, NbRenderableContainer } from '../cdk';
import { animate, state, style, transition, trigger } from '@angular/animations';


Expand Down Expand Up @@ -50,7 +50,7 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
]),
],
})
export class NbTooltipComponent {
export class NbTooltipComponent implements NbRenderableContainer {

@Input()
content: string;
Expand All @@ -77,4 +77,10 @@ export class NbTooltipComponent {
get statusClass() {
return this.context.status ? `${this.context.status}-tooltip` : '';
}

/**
* The method is empty since we don't need to do anything additionally
* render is handled by change detection
*/
renderContent() {}
}
143 changes: 40 additions & 103 deletions src/framework/theme/components/tooltip/tooltip.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

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

import {
createContainer,
NbAdjustableConnectedPositionStrategy,
NbAdjustment,
NbOverlayRef,
NbOverlayService,
NbPosition,
NbPositionBuilderService,
NbTrigger,
NbTriggerStrategy,
NbTriggerStrategyBuilderService,
patch,
} from '../cdk';
import { NB_DOCUMENT } from '../../theme.options';
import { AfterViewInit, Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';

import { NbAdjustment, NbDynamicOverlay, NbDynamicOverlayHandler, NbPosition, NbTrigger } from '../cdk';
import { NbTooltipComponent } from './tooltip.component';

/**
Expand Down Expand Up @@ -72,8 +49,11 @@ import { NbTooltipComponent } from './tooltip.component';
* - Focus mode is applied when user focuses the element.
* - Noop mode - the component won't react to the user interaction.
*/
@Directive({ selector: '[nbTooltip]' })
export class NbTooltipDirective implements AfterViewInit, OnDestroy {
@Directive({
selector: '[nbTooltip]',
providers: [NbDynamicOverlayHandler, NbDynamicOverlay],
})
export class NbTooltipDirective implements OnInit, OnChanges, AfterViewInit, OnDestroy {

context: Object = {};

Expand Down Expand Up @@ -102,7 +82,7 @@ export class NbTooltipDirective implements AfterViewInit, OnDestroy {
*/
@Input('nbTooltipIcon')
set icon(icon: string) {
this.context = Object.assign(this.context, { icon });
this.context = Object.assign(this.context, {icon});
}

/**
Expand All @@ -111,7 +91,7 @@ export class NbTooltipDirective implements AfterViewInit, OnDestroy {
*/
@Input('nbTooltipStatus')
set status(status: string) {
this.context = Object.assign(this.context, { status });
this.context = Object.assign(this.context, {status});
}

/**
Expand All @@ -121,98 +101,55 @@ export class NbTooltipDirective implements AfterViewInit, OnDestroy {
@Input('nbTooltipTrigger')
trigger: NbTrigger = NbTrigger.HINT;

protected ref: NbOverlayRef;
protected container: ComponentRef<any>;
protected positionStrategy: NbAdjustableConnectedPositionStrategy;
protected alive: boolean = true;

constructor(@Inject(NB_DOCUMENT) protected document,
private hostRef: ElementRef,
private positionBuilder: NbPositionBuilderService,
protected triggerStrategyBuilder: NbTriggerStrategyBuilderService,
private overlay: NbOverlayService,
private componentFactoryResolver: ComponentFactoryResolver) {
private dynamicOverlay: NbDynamicOverlay;

constructor(private hostRef: ElementRef,
private dynamicOverlayHandler: NbDynamicOverlayHandler) {
}

ngOnInit() {
this.dynamicOverlayHandler
.host(this.hostRef)
.componentType(NbTooltipComponent)
.offset(8);
}

ngOnChanges() {
this.rebuild();
}

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

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.openTooltip();
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 openTooltip() {
this.container = createContainer(this.ref, NbTooltipComponent, {
position: this.position,
content: this.content,
context: this.context,
cfr: this.componentFactoryResolver,
}, 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)
.offset(8);
}

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)
.content(this.content)
.context(this.context);
}
}
Loading

0 comments on commit 9ce7019

Please sign in to comment.