Skip to content

Commit 6dbce1d

Browse files
authored
feat(popover, tooltip): add shown state api (#1998)
1 parent d8689c1 commit 6dbce1d

File tree

8 files changed

+323
-30
lines changed

8 files changed

+323
-30
lines changed

src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay-handler.spec.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { NbDynamicOverlay } from './dynamic-overlay';
2222
import { NbOverlayContent } from '../overlay-service';
2323
import { NbDynamicOverlayChange, NbDynamicOverlayHandler } from './dynamic-overlay-handler';
2424
import { NbTrigger, NbTriggerStrategy, NbTriggerStrategyBuilderService } from '../overlay-trigger';
25+
import { NbOverlayConfig } from '@nebular/theme/components/cdk/overlay/mapping';
2526

2627
@Component({ template: '' })
2728
export class NbDynamicOverlayMockComponent implements NbRenderableContainer {
@@ -43,18 +44,21 @@ export class NbMockDynamicOverlay {
4344
_context: Object = {};
4445
_content: NbOverlayContent;
4546
_positionStrategy: NbAdjustableConnectedPositionStrategy;
47+
_overlayConfig: NbOverlayConfig;
4648

4749
constructor() {}
4850

4951
create(componentType: Type<NbRenderableContainer>,
5052
content: NbOverlayContent,
5153
context: Object,
52-
positionStrategy: NbAdjustableConnectedPositionStrategy) {
54+
positionStrategy: NbAdjustableConnectedPositionStrategy,
55+
overlayConfig: NbOverlayConfig) {
5356

5457
this.setContext(context);
5558
this.setContent(content);
5659
this.setComponent(componentType);
5760
this.setPositionStrategy(positionStrategy);
61+
this.setOverlayConfig(overlayConfig);
5862

5963
return this;
6064
}
@@ -71,6 +75,10 @@ export class NbMockDynamicOverlay {
7175
this._componentType = componentType;
7276
}
7377

78+
setOverlayConfig(overlayConfig: NbOverlayConfig) {
79+
this._overlayConfig = overlayConfig;
80+
}
81+
7482
setContentAndContext(content: NbOverlayContent, context: Object) {
7583
this._content = content;
7684
this._context = context;
@@ -516,4 +524,15 @@ describe('dynamic-overlay-handler', () => {
516524
expect(positionBuilder._position).toBe(NbPosition.LEFT);
517525
expect(positionBuilder._adjustment).toBe(NbAdjustment.HORIZONTAL);
518526
});
527+
528+
it('should set and update overlay config', () => {
529+
let overlayConfig: NbOverlayConfig = { panelClass: 'custom-class' };
530+
531+
let dynamic = configure().overlayConfig(overlayConfig).build();
532+
expect(dynamic._overlayConfig).toEqual(jasmine.objectContaining(overlayConfig));
533+
534+
overlayConfig = { panelClass: 'other-custom-class' };
535+
dynamic = configure().overlayConfig(overlayConfig).rebuild();
536+
expect(dynamic._overlayConfig).toEqual(jasmine.objectContaining(overlayConfig));
537+
});
519538
});

src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay-handler.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { NbRenderableContainer } from '../overlay-container';
1111
import { NbOverlayContent } from '../overlay-service';
1212
import { NbDynamicOverlay } from './dynamic-overlay';
13+
import { NbOverlayConfig } from '../mapping';
1314

1415
export class NbDynamicOverlayChange extends SimpleChange {
1516

@@ -33,6 +34,7 @@ export class NbDynamicOverlayHandler {
3334
protected _position: NbPosition = NbPosition.TOP;
3435
protected _adjustment: NbAdjustment = NbAdjustment.NOOP;
3536
protected _offset: number = 15;
37+
protected _overlayConfig: NbOverlayConfig = {};
3638

3739
protected dynamicOverlay: NbDynamicOverlay;
3840
protected triggerStrategy: NbTriggerStrategy;
@@ -94,6 +96,12 @@ export class NbDynamicOverlayHandler {
9496
return this;
9597
}
9698

99+
overlayConfig(overlayConfig: NbOverlayConfig) {
100+
this.changes.overlayConfig = new NbDynamicOverlayChange(this._overlayConfig, overlayConfig);
101+
this._overlayConfig = overlayConfig;
102+
return this;
103+
}
104+
97105
build() {
98106
if (!this._componentType || !this._host) {
99107
throw Error(`NbDynamicOverlayHandler: at least 'componentType' and 'host' should be
@@ -104,6 +112,7 @@ export class NbDynamicOverlayHandler {
104112
this._content,
105113
this._context,
106114
this.createPositionStrategy(),
115+
this._overlayConfig,
107116
);
108117

109118
this.connect();
@@ -139,6 +148,10 @@ export class NbDynamicOverlayHandler {
139148
this.dynamicOverlay.setComponent(this._componentType);
140149
}
141150

151+
if (this.isOverlayConfigUpdateRequired()) {
152+
this.dynamicOverlay.setOverlayConfig(this._overlayConfig);
153+
}
154+
142155
this.clearChanges();
143156
return this.dynamicOverlay;
144157
}
@@ -203,6 +216,10 @@ export class NbDynamicOverlayHandler {
203216
return this.isComponentTypeUpdated();
204217
}
205218

219+
private isOverlayConfigUpdateRequired(): boolean {
220+
return this.isOverlayConfigUpdated();
221+
}
222+
206223
protected isComponentTypeUpdated(): boolean {
207224
return this.changes.componentType && this.changes.componentType.isChanged();
208225
}
@@ -235,6 +252,10 @@ export class NbDynamicOverlayHandler {
235252
return this.changes.offset && this.changes.offset.isChanged();
236253
}
237254

255+
protected isOverlayConfigUpdated(): boolean {
256+
return this.changes.overlayConfig && this.changes.overlayConfig.isChanged();
257+
}
258+
238259
protected clearChanges() {
239260
this.changes = {};
240261
}

src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay.spec.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,16 @@ describe('dynamic-overlay', () => {
267267
expect(instance.content).toBe(newContent);
268268
});
269269

270+
it('should set overlay config', () => {
271+
const overlayConfig: NbOverlayConfig = { panelClass: 'additional-overlay-class' };
272+
const createOverlaySpy = spyOn(overlayService, 'create').and.callThrough();
273+
274+
dynamicOverlay.setOverlayConfig(overlayConfig);
275+
dynamicOverlay.show();
276+
277+
expect(createOverlaySpy).toHaveBeenCalledWith(jasmine.objectContaining(overlayConfig));
278+
});
279+
270280
it('should return container', () => {
271281
dynamicOverlay.show();
272282
expect(dynamicOverlay.getContainer()).toBe(container as any);
@@ -319,29 +329,25 @@ describe('dynamic-overlay', () => {
319329
});
320330

321331
it('should set component', () => {
322-
const detachSpy = spyOn(ref, 'detach').and.callThrough();
323332
const disposeSpy = spyOn(ref, 'dispose').and.callThrough();
324333
const attachSpy = spyOn(ref, 'attach').and.callThrough();
325334
const hasAttacheSpy = spyOn(ref, 'hasAttached');
326335

327336
dynamicOverlay.setComponent(NbDynamicOverlayMock2Component);
328337

329-
expect(detachSpy).toHaveBeenCalledTimes(0);
330338
expect(disposeSpy).toHaveBeenCalledTimes(0);
331339
expect(attachSpy).toHaveBeenCalledTimes(0);
332340

333341
dynamicOverlay.show();
334342
hasAttacheSpy.and.returnValue(true);
335343

336344
expect(ref.portal.component).toBe(NbDynamicOverlayMock2Component);
337-
expect(detachSpy).toHaveBeenCalledTimes(0);
338345
expect(disposeSpy).toHaveBeenCalledTimes(0);
339346
expect(attachSpy).toHaveBeenCalledTimes(1);
340347

341348
dynamicOverlay.setComponent(NbDynamicOverlayMockComponent);
342349

343350
expect(ref.portal.component).toBe(NbDynamicOverlayMockComponent);
344-
expect(detachSpy).toHaveBeenCalledTimes(1);
345351
expect(disposeSpy).toHaveBeenCalledTimes(1);
346352
expect(attachSpy).toHaveBeenCalledTimes(2);
347353

@@ -350,7 +356,6 @@ describe('dynamic-overlay', () => {
350356

351357
dynamicOverlay.setComponent(NbDynamicOverlayMock2Component);
352358

353-
expect(detachSpy).toHaveBeenCalledTimes(3);
354359
expect(disposeSpy).toHaveBeenCalledTimes(2);
355360
expect(attachSpy).toHaveBeenCalledTimes(2);
356361
});

src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ComponentFactoryResolver, ComponentRef, Injectable, NgZone, Type } from '@angular/core';
2-
import { filter, takeUntil, takeWhile } from 'rxjs/operators';
3-
import { Subject } from 'rxjs';
2+
import { filter, takeUntil, takeWhile, distinctUntilChanged } from 'rxjs/operators';
3+
import { Subject, BehaviorSubject, Observable } from 'rxjs';
44

55
import {
66
NbAdjustableConnectedPositionStrategy,
@@ -9,7 +9,7 @@ import {
99

1010
import { NbRenderableContainer } from '../overlay-container';
1111
import { createContainer, NbOverlayContent, NbOverlayService, patch } from '../overlay-service';
12-
import { NbOverlayRef, NbOverlayContainer } from '../mapping';
12+
import { NbOverlayRef, NbOverlayContainer, NbOverlayConfig } from '../mapping';
1313

1414
export interface NbDynamicOverlayController {
1515
show();
@@ -27,14 +27,20 @@ export class NbDynamicOverlay {
2727
protected context: Object = {};
2828
protected content: NbOverlayContent;
2929
protected positionStrategy: NbAdjustableConnectedPositionStrategy;
30+
protected overlayConfig: NbOverlayConfig = {};
3031

3132
protected positionStrategyChange$ = new Subject();
33+
protected isShown$ = new BehaviorSubject<boolean>(false);
3234
protected alive = true;
3335

3436
get isAttached(): boolean {
3537
return this.ref && this.ref.hasAttached();
3638
}
3739

40+
get isShown(): Observable<boolean> {
41+
return this.isShown$.pipe(distinctUntilChanged());
42+
}
43+
3844
constructor(
3945
protected overlay: NbOverlayService,
4046
protected componentFactoryResolver: ComponentFactoryResolver,
@@ -45,11 +51,13 @@ export class NbDynamicOverlay {
4551
create(componentType: Type<NbRenderableContainer>,
4652
content: NbOverlayContent,
4753
context: Object,
48-
positionStrategy: NbAdjustableConnectedPositionStrategy) {
54+
positionStrategy: NbAdjustableConnectedPositionStrategy,
55+
overlayConfig: NbOverlayConfig = {}) {
4956

5057
this.setContentAndContext(content, context);
5158
this.setComponent(componentType);
5259
this.setPositionStrategy(positionStrategy);
60+
this.setOverlayConfig(overlayConfig);
5361

5462
return this;
5563
}
@@ -82,11 +90,10 @@ export class NbDynamicOverlay {
8290
this.componentType = componentType;
8391

8492
// in case the component is shown we recreate it and show it back
85-
if (this.ref && this.isAttached) {
86-
this.dispose();
93+
const wasAttached = this.isAttached;
94+
this.disposeOverlayRef();
95+
if (wasAttached) {
8796
this.show();
88-
} else if (this.ref && !this.isAttached) {
89-
this.dispose();
9097
}
9198
}
9299

@@ -108,6 +115,16 @@ export class NbDynamicOverlay {
108115
}
109116
}
110117

118+
setOverlayConfig(overlayConfig: NbOverlayConfig) {
119+
this.overlayConfig = overlayConfig;
120+
121+
const wasAttached = this.isAttached;
122+
this.disposeOverlayRef();
123+
if (wasAttached) {
124+
this.show();
125+
}
126+
}
127+
111128
show() {
112129
if (!this.ref) {
113130
this.createOverlay();
@@ -120,6 +137,8 @@ export class NbDynamicOverlay {
120137
this.disposeOverlayRef();
121138
return this.show();
122139
}
140+
141+
this.isShown$.next(true);
123142
}
124143

125144
hide() {
@@ -129,6 +148,8 @@ export class NbDynamicOverlay {
129148

130149
this.ref.detach();
131150
this.container = null;
151+
152+
this.isShown$.next(false);
132153
}
133154

134155
toggle() {
@@ -143,6 +164,8 @@ export class NbDynamicOverlay {
143164
this.alive = false;
144165
this.hide();
145166
this.disposeOverlayRef();
167+
this.isShown$.complete();
168+
this.positionStrategyChange$.complete();
146169
}
147170

148171
getContainer() {
@@ -153,6 +176,7 @@ export class NbDynamicOverlay {
153176
this.ref = this.overlay.create({
154177
positionStrategy: this.positionStrategy,
155178
scrollStrategy: this.overlay.scrollStrategies.reposition(),
179+
...this.overlayConfig,
156180
});
157181
this.updatePositionWhenStable();
158182
}

0 commit comments

Comments
 (0)