Skip to content

Commit 69c0135

Browse files
committed
feat(overlay): do not move overlay element out of its place
1 parent 724248f commit 69c0135

23 files changed

Lines changed: 258 additions & 270 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,9 @@ All notable changes for each version of this project will be documented in this
77
### New Features
88

99
- `IgxOverlayService`
10-
- Added `moveToOverlayContainer` property to `OverlaySettings`. When set to `false` (default), the overlay element stays in its original DOM position using the HTML Popover API to promote it to the top layer in-place. When set to `true`, the element is moved to the overlay container.
10+
- The overlay service now always renders content in place using the HTML Popover API, eliminating the need for outlet containers.
1111

12-
```typescript
13-
const overlaySettings: OverlaySettings = {
14-
moveToOverlayContainer: false,
15-
positionStrategy: new ConnectedPositioningStrategy(),
16-
scrollStrategy: new AbsoluteScrollStrategy()
17-
};
18-
```
19-
20-
- **Deprecation** - The `outlet` property in `OverlaySettings` has been deprecated and will be removed in a future version. Set `moveToOverlayContainer` to `false` (or leave it unset) to keep the overlay in its original DOM position. The `IgxOverlayOutletDirective` and `igxToggleOutlet` input on `IgxToggleActionDirective` are also deprecated.
12+
- **Deprecation** - The `outlet` property in `OverlaySettings`, `IgxOverlayOutletDirective`, and `igxToggleOutlet` input on `IgxToggleActionDirective` are all deprecated. The overlay service now always renders content in place using the HTML Popover API. These will be removed in a future version.
2113

2214
## 21.1.0
2315

projects/igniteui-angular/core/src/services/overlay/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ this.overlay.show(component, overlaySettings);
7878
| scrollStrategy | IScrollStrategy | Scroll strategy to use with this settings |
7979
| modal | boolean | Set if the overlay should be in modal mode |
8080
| closeOnOutsideClick | boolean | Set if the overlay should closed on outside click |
81-
| outlet | IgxOverlayOutletDirective or ElementRef | **Deprecated.** Set `moveToOverlayContainer` to `false` (or leave it unset) to keep the overlay in its original DOM position. Sets the outlet container to attach the overlay to. |
82-
| moveToOverlayContainer | boolean | When set to `false` (default), the overlay element stays in its original DOM position using the HTML Popover API to promote the element to the top layer in-place. When set to `true`, the element is moved to the overlay container. |
81+
| outlet | IgxOverlayOutletDirective or ElementRef | **Deprecated.** No longer has any effect. The overlay service now always renders content in place using the HTML Popover API. This property will be removed in a future version. |
8382

8483
###### PositionSettings
8584

@@ -136,7 +135,7 @@ this.overlay.show(component, overlaySettings);
136135
| Name | Description | Parameters |
137136
|-----------------|---------------------------------------------------------------------------------|------------|
138137
|getPointFromPositionsSettings| Calculates the point from which the overlay should start showing |settings |
139-
|createAbsoluteOverlaySettings| Creates overlay settings with global or container position strategy based on a preset position settings. **Note:** The `outlet` parameter is deprecated; set `moveToOverlayContainer` to `false` in the returned `OverlaySettings` to keep the overlay in place. |position?, outlet?|
138+
|createAbsoluteOverlaySettings| Creates overlay settings with global or container position strategy based on a preset position settings. **Note:** The `outlet` parameter is deprecated and no longer has any effect. The overlay service now always renders content in place. |position?, outlet?|
140139
|createRelativeOverlaySettings| Creates overlay settings with auto, connected or elastic position strategy based on a preset position settings |target, strategy?, position?|
141140

142141

projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts

Lines changed: 170 additions & 141 deletions
Large diffs are not rendered by default.

projects/igniteui-angular/core/src/services/overlay/overlay.ts

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ export class IgxOverlayService implements OnDestroy {
117117

118118
private _componentId = 0;
119119
private _overlayInfos: OverlayInfo[] = [];
120-
private _overlayElement: HTMLElement;
121120
private _document: Document;
122121
private _keyPressEventListener: Subscription;
123122
private destroy$ = new Subject<boolean>();
@@ -334,14 +333,12 @@ export class IgxOverlayService implements OnDestroy {
334333
info.initialSize = { width: elementRect.width, height: elementRect.height };
335334
// Get the size before moving the container into the overlay so that it does not forget about inherited styles.
336335
this.getComponentSize(info);
337-
if (!info.settings.moveToOverlayContainer &&
338-
info.elementRef.nativeElement.parentElement &&
339-
!info.settings.outlet) {
336+
if (info.settings.outlet) {
337+
this.moveElementToOutlet(info);
338+
} else if (info.elementRef.nativeElement.parentElement) {
340339
this.wrapElementInPlace(info);
341-
info.wrappedInPlace = true;
342340
} else {
343-
info.hook = this.placeElementHook(info.elementRef.nativeElement);
344-
this.moveElementToOverlay(info);
341+
this.appendElementToDocument(info);
345342
}
346343
// Update the container size after wrapping/moving if there is size.
347344
if (info.size) {
@@ -626,6 +623,7 @@ export class IgxOverlayService implements OnDestroy {
626623
if (createSettings) {
627624
({ injector: elementInjector, ...overlaySettings } = createSettings);
628625
}
626+
console.warn('Overlay component is created without a ViewContainerRef. The element will be outside the Angular component tree and may not inherit styles. Prefer using the ViewContainerRef overload or provide an outlet.');
629627
dynamicComponent = createComponent(component, { environmentInjector, elementInjector });
630628
this._appRef.attachView(dynamicComponent.hostView);
631629
}
@@ -656,10 +654,12 @@ export class IgxOverlayService implements OnDestroy {
656654
return hook;
657655
}
658656

659-
private moveElementToOverlay(info: OverlayInfo) {
657+
private moveElementToOutlet(info: OverlayInfo) {
658+
info.hook = this.placeElementHook(info.elementRef.nativeElement);
660659
info.wrapperElement = this.getWrapperElement();
661660
const contentElement = this.getContentElement(info.wrapperElement, info.settings.modal);
662-
this.getOverlayElement(info).appendChild(info.wrapperElement);
661+
const outlet = info.settings.outlet.nativeElement || info.settings.outlet;
662+
outlet.appendChild(info.wrapperElement);
663663
contentElement.appendChild(info.elementRef.nativeElement);
664664
}
665665

@@ -672,6 +672,14 @@ export class IgxOverlayService implements OnDestroy {
672672
contentElement.appendChild(element);
673673
}
674674

675+
private appendElementToDocument(info: OverlayInfo) {
676+
info.wrapperElement = this.getWrapperElement();
677+
const contentElement = this.getContentElement(info.wrapperElement, info.settings.modal);
678+
this._document.body.appendChild(info.wrapperElement);
679+
contentElement.appendChild(info.elementRef.nativeElement);
680+
info.appendedToBody = true;
681+
}
682+
675683
private getWrapperElement(): HTMLElement {
676684
const wrapper: HTMLElement = this._document.createElement('div');
677685
wrapper.classList.add('igx-overlay__wrapper');
@@ -700,18 +708,6 @@ export class IgxOverlayService implements OnDestroy {
700708
return content;
701709
}
702710

703-
private getOverlayElement(info: OverlayInfo): HTMLElement {
704-
if (info.settings.outlet) {
705-
return info.settings.outlet.nativeElement || info.settings.outlet;
706-
}
707-
if (!this._overlayElement) {
708-
this._overlayElement = this._document.createElement('div');
709-
this._overlayElement.classList.add('igx-overlay');
710-
this._document.body.appendChild(this._overlayElement);
711-
}
712-
return this._overlayElement;
713-
}
714-
715711
private updateSize(info: OverlayInfo) {
716712
if (info.componentRef) {
717713
// if we are positioning component this is first time it gets visible
@@ -750,14 +746,18 @@ export class IgxOverlayService implements OnDestroy {
750746

751747
private cleanUp(info: OverlayInfo) {
752748
const child: HTMLElement = info.elementRef.nativeElement;
753-
if (info.wrappedInPlace) {
749+
if (info.appendedToBody) {
750+
// Element was appended to document body as a fallback (no outlet, no parent).
751+
// Just remove the wrapper; the dynamic component will be destroyed below.
752+
info.wrapperElement?.parentElement?.removeChild(info.wrapperElement);
753+
} else if (!info.settings.outlet) {
754754
// Unwrap: move element back to wrapper's parent position, then remove wrapper
755755
if (info.wrapperElement?.parentElement) {
756756
info.wrapperElement.parentElement.insertBefore(child, info.wrapperElement);
757757
info.wrapperElement.parentElement.removeChild(info.wrapperElement);
758758
}
759759
} else {
760-
const outlet = this.getOverlayElement(info);
760+
const outlet = info.settings.outlet.nativeElement || info.settings.outlet;
761761
// if same element is shown in other overlay outlet will not contain
762762
// the element and we should not remove it form outlet
763763
if (outlet.contains(child)) {
@@ -778,12 +778,7 @@ export class IgxOverlayService implements OnDestroy {
778778
const index = this._overlayInfos.indexOf(info);
779779
this._overlayInfos.splice(index, 1);
780780

781-
// this._overlayElement.parentElement check just for tests that manually delete the element
782781
if (this._overlayInfos.length === 0) {
783-
if (this._overlayElement && this._overlayElement.parentElement) {
784-
this._overlayElement.parentElement.removeChild(this._overlayElement);
785-
this._overlayElement = null;
786-
}
787782
this.removeCloseOnEscapeListener();
788783
}
789784

projects/igniteui-angular/core/src/services/overlay/position/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Position strategies determine where to display the component in the provided Igx
77
|:---------------------------|:-------------------------|
88
| HorizontalAlignment.Center | VerticalAlignment.Middle |
99

10-
2) **Container** - Positions the element inside the containing outlet based on the directions passed in through PositionSettings. These are Top/Middle/Bottom for verticalDirection and Left/Center/Right for horizontalDirection. **Note:** The `outlet` property in `OverlaySettings` is deprecated; set `moveToOverlayContainer` to `false` (or leave it unset) to keep overlays in their original DOM position instead. Defaults to:
10+
2) **Container** - Positions the element inside the containing outlet based on the directions passed in through PositionSettings. These are Top/Middle/Bottom for verticalDirection and Left/Center/Right for horizontalDirection. **Note:** The `outlet` property in `OverlaySettings` is deprecated and no longer has any effect. The overlay service now always renders content in place using the HTML Popover API. Defaults to:
1111

1212
| horizontalDirection | verticalDirection |
1313
|:---------------------------|:-------------------------|

projects/igniteui-angular/core/src/services/overlay/utilities.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,9 @@ export interface OverlaySettings {
132132
closeOnEscape?: boolean;
133133
/* blazorSuppress */
134134
/**
135-
* @deprecated The `outlet` property is deprecated. There is no direct 1:1 replacement for attaching overlays to an arbitrary outlet
136-
* container. If you only used `outlet` to keep the overlay in its current DOM position (i.e. not moved to the overlay container),
137-
* set `moveToOverlayContainer` to `false` or leave it unset, as `false` is the default.
138-
* Set the outlet container to attach the overlay to.
135+
* @deprecated The `outlet` property is deprecated and no longer has any effect.
136+
* The overlay service now always renders content in place using the HTML Popover API.
137+
* This property will be removed in a future version.
139138
*/
140139
outlet?: IgxOverlayOutletDirective | ElementRef;
141140
/**
@@ -144,13 +143,6 @@ export interface OverlaySettings {
144143
* Clicking on the elements in this collection will not close the overlay when closeOnOutsideClick = true.
145144
*/
146145
excludeFromOutsideClick?: HTMLElement[];
147-
/**
148-
* When set to false (default), the overlay element stays in its original DOM position
149-
* using the HTML Popover API to promote the element to the top layer in-place.
150-
* When set to true, the element is moved to the overlay container.
151-
* Defaults to false.
152-
*/
153-
moveToOverlayContainer?: boolean;
154146
}
155147

156148
export interface OverlayEventArgs extends IBaseEventArgs {
@@ -212,8 +204,8 @@ export interface OverlayInfo {
212204
transformY?: number;
213205
event?: Event;
214206
wrapperElement?: HTMLElement;
215-
wrappedInPlace?: boolean;
216-
size?: string
207+
size?: string;
208+
appendedToBody?: boolean
217209
}
218210

219211
/** @hidden */

projects/igniteui-angular/date-picker/src/date-picker/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ The date picker also supports binding through `ngModel` if two-way date-bind is
113113
| `overlaySettings` | Changes the default overlay settings used by the `IgxDatePickerComponent`. | OverlaySettings |
114114
| `placeholder` | Sets the placeholder text for empty input. | string |
115115
| `disabled` | Disables or enables the picker. | boolean |
116-
| `outlet` | **Deprecated.** Set `moveToOverlayContainer` to `false` in `overlaySettings` (or leave it unset) to keep the overlay in its original DOM position. The container used for the pop up element. | IgxOverlayOutletDirective \| ElementRef |
116+
| `outlet` | **Deprecated.** No longer has any effect. The overlay service now always renders content in place using the HTML Popover API. This property will be removed in a future version. | IgxOverlayOutletDirective \| ElementRef |
117117
| `type` | Determines how the picker will be styled. | IgxInputGroupType |
118118
| `spinLoop` | Determines if the currently spun date segment should loop over. | boolean |
119119
| `spinDelta` | Delta values used to increment or decrement each editor date part on spin actions. All values default to `1`. | DatePartDeltas |

projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,9 @@ export class IgxDatePickerComponent extends PickerBaseDirective implements Contr
244244
public spinDelta: Pick<DatePartDeltas, 'date' | 'month' | 'year'>;
245245

246246
/**
247-
* @deprecated The `outlet` property is deprecated. There is no direct 1:1 replacement for attaching overlays to an arbitrary outlet
248-
* container. If you only used `outlet` to keep the overlay in its current DOM position (i.e. not moved to the overlay container),
249-
* set `moveToOverlayContainer` to `false` in `overlaySettings` to keep the overlay in its original DOM position.
247+
* @deprecated The `outlet` property is deprecated and no longer has any effect.
248+
* The overlay service now always renders content in place using the HTML Popover API.
249+
* This property will be removed in a future version.
250250
*
251251
* Gets/Sets the container used for the popup element.
252252
*
@@ -510,14 +510,12 @@ export class IgxDatePickerComponent extends PickerBaseDirective implements Contr
510510
positionStrategy: new AutoPositionStrategy({
511511
openAnimation: fadeIn,
512512
closeAnimation: fadeOut
513-
}),
514-
moveToOverlayContainer: false
513+
})
515514
};
516515
private _dialogOverlaySettings: OverlaySettings = {
517516
closeOnOutsideClick: true,
518517
modal: true,
519-
closeOnEscape: true,
520-
moveToOverlayContainer: false
518+
closeOnEscape: true
521519
};
522520
private _calendarFormat: IFormattingOptions = {
523521
day: 'numeric',

projects/igniteui-angular/date-picker/src/date-picker/picker-base.directive.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,9 @@ export abstract class PickerBaseDirective implements IToggleView, EditorProvider
186186
}
187187

188188
/**
189-
* @deprecated The `outlet` property is deprecated. There is no direct 1:1 replacement for attaching overlays to an arbitrary outlet
190-
* container. If you only used `outlet` to keep the overlay in its current DOM position (i.e. not moved to the overlay container),
191-
* set `moveToOverlayContainer` to `false` in `overlaySettings` to keep the overlay in its original DOM position.
189+
* @deprecated The `outlet` property is deprecated and no longer has any effect.
190+
* The overlay service now always renders content in place using the HTML Popover API.
191+
* This property will be removed in a future version.
192192
*
193193
* The container used for the pop-up element.
194194
*

projects/igniteui-angular/date-picker/src/date-range-picker/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ With projected inputs:
116116
| mode | PickerInteractionMode | Sets whether `IgxDateRangePickerComponent` is in dialog or dropdown mode. Default is `dialog` |
117117
| minValue | Date \| string | The minimum value in a valid range. |
118118
| maxValue | Date \| string | The maximum value in a valid range. |
119-
| outlet | IgxOverlayOutletDirective \| ElementRef<any> | **Deprecated.** Set `moveToOverlayContainer` to `false` in `overlaySettings` (or leave it unset) to keep the overlay in its original DOM position. Gets/Sets the container used for the popup element.
119+
| outlet | IgxOverlayOutletDirective \| ElementRef<any> | **Deprecated.** No longer has any effect. The overlay service now always renders content in place using the HTML Popover API. This property will be removed in a future version.
120120
| overlaySettings | OverlaySettings | Changes the default overlay settings used by the `IgxDateRangePickerComponent`. |
121121
| placeholder | string | Sets the `placeholder` for single-input `IgxDateRangePickerComponent`. |
122122
| weekStart | number | Sets the start day of the week. Can be assigned to a numeric value or to `WEEKDAYS` enum value. |

0 commit comments

Comments
 (0)