Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions goldens/cdk/overlay/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
// (undocumented)
static ngAcceptInputType_push: unknown;
// (undocumented)
static ngAcceptInputType_usePopover: unknown;
// (undocumented)
ngOnChanges(changes: SimpleChanges): void;
// (undocumented)
ngOnDestroy(): void;
Expand All @@ -98,7 +96,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
push: boolean;
scrollStrategy: ScrollStrategy;
transformOriginSelector: string;
usePopover: boolean;
usePopover: FlexibleOverlayPopoverLocation | null;
viewportMargin: ViewportMargin;
width: number | string;
// (undocumented)
Expand Down Expand Up @@ -150,7 +148,7 @@ export interface CdkConnectedOverlayConfig {
// (undocumented)
transformOriginSelector?: string;
// (undocumented)
usePopover?: boolean;
usePopover?: FlexibleOverlayPopoverLocation | null;
// (undocumented)
viewportMargin?: ViewportMargin;
// (undocumented)
Expand Down Expand Up @@ -267,7 +265,7 @@ export function createCloseScrollStrategy(injector: Injector, config?: CloseScro
export function createFlexibleConnectedPositionStrategy(injector: Injector, origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy;

// @public
export function createGlobalPositionStrategy(injector: Injector): GlobalPositionStrategy;
export function createGlobalPositionStrategy(_injector: Injector): GlobalPositionStrategy;

// @public
export function createNoopScrollStrategy(): NoopScrollStrategy;
Expand All @@ -286,7 +284,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
// (undocumented)
detach(): void;
dispose(): void;
getPopoverInsertionPoint(): Element;
getPopoverInsertionPoint(): Element | null;
_origin: FlexibleConnectedPositionStrategyOrigin;
positionChanges: Observable<ConnectedOverlayPositionChange>;
get positions(): ConnectionPositionPair[];
Expand All @@ -298,6 +296,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
withFlexibleDimensions(flexibleDimensions?: boolean): this;
withGrowAfterOpen(growAfterOpen?: boolean): this;
withLockedPosition(isLocked?: boolean): this;
withPopoverLocation(location: FlexibleOverlayPopoverLocation): this;
withPositions(positions: ConnectedPosition[]): this;
withPush(canPush?: boolean): this;
withScrollableContainers(scrollables: CdkScrollable[]): this;
Expand All @@ -311,6 +310,9 @@ export type FlexibleConnectedPositionStrategyOrigin = ElementRef | Element | (Po
height?: number;
});

// @public
export type FlexibleOverlayPopoverLocation = 'global' | 'inline';

// @public
export class FullscreenOverlayContainer extends OverlayContainer implements OnDestroy {
constructor(...args: unknown[]);
Expand All @@ -327,7 +329,6 @@ export class FullscreenOverlayContainer extends OverlayContainer implements OnDe

// @public
export class GlobalPositionStrategy implements PositionStrategy {
constructor(injector?: Injector);
apply(): void;
// (undocumented)
attach(overlayRef: OverlayRef): void;
Expand All @@ -336,7 +337,6 @@ export class GlobalPositionStrategy implements PositionStrategy {
centerVertically(offset?: string): this;
dispose(): void;
end(value?: string): this;
getPopoverInsertionPoint(): Element;
// @deprecated
height(value?: string): this;
left(value?: string): this;
Expand Down Expand Up @@ -525,7 +525,7 @@ export interface PositionStrategy {
attach(overlayRef: OverlayRef): void;
detach?(): void;
dispose(): void;
getPopoverInsertionPoint?(): Element;
getPopoverInsertionPoint?(): Element | null;
}

// @public
Expand Down
4 changes: 3 additions & 1 deletion src/cdk/overlay/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,13 @@ $backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
background: none;
border: none;
padding: 0;
color: inherit;
outline: 0;
overflow: visible;
position: fixed;
pointer-events: none;
white-space: normal;
line-height: normal;
text-decoration: none;

// These are important so the overlay can be measured before it's fully inserted.
width: 100%;
Expand Down
12 changes: 7 additions & 5 deletions src/cdk/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
createFlexibleConnectedPositionStrategy,
FlexibleConnectedPositionStrategy,
FlexibleConnectedPositionStrategyOrigin,
FlexibleOverlayPopoverLocation,
} from './position/flexible-connected-position-strategy';
import {createRepositionScrollStrategy, ScrollStrategy} from './scroll/index';

Expand Down Expand Up @@ -127,7 +128,7 @@ export interface CdkConnectedOverlayConfig {
growAfterOpen?: boolean;
push?: boolean;
disposeOnNavigation?: boolean;
usePopover?: boolean;
usePopover?: FlexibleOverlayPopoverLocation | null;
matchWidth?: boolean;
}

Expand Down Expand Up @@ -251,8 +252,8 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
disposeOnNavigation: boolean = false;

/** Whether the connected overlay should be rendered inside a popover element or the overlay container. */
@Input({alias: 'cdkConnectedOverlayUsePopover', transform: booleanAttribute})
usePopover: boolean = false;
@Input({alias: 'cdkConnectedOverlayUsePopover'})
usePopover: FlexibleOverlayPopoverLocation | null = null;

/** Whether the overlay should match the trigger's width. */
@Input({alias: 'cdkConnectedOverlayMatchWidth', transform: booleanAttribute})
Expand Down Expand Up @@ -377,7 +378,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
scrollStrategy: this.scrollStrategy,
hasBackdrop: this.hasBackdrop,
disposeOnNavigation: this.disposeOnNavigation,
usePopover: this.usePopover,
usePopover: !!this.usePopover,
});

if (this.height || this.height === 0) {
Expand Down Expand Up @@ -423,7 +424,8 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
.withGrowAfterOpen(this.growAfterOpen)
.withViewportMargin(this.viewportMargin)
.withLockedPosition(this.lockPosition)
.withTransformOriginOn(this.transformOriginSelector);
.withTransformOriginOn(this.transformOriginSelector)
.withPopoverLocation(this.usePopover === 'global' ? 'global' : 'inline');
}

/** Returns the position strategy of the overlay to be set on the overlay config */
Expand Down
8 changes: 6 additions & 2 deletions src/cdk/overlay/overlay-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,12 @@ export class OverlayRef implements PortalOutlet {

private _attachHost() {
if (!this._host.parentElement) {
if (this._config.usePopover && this._positionStrategy?.getPopoverInsertionPoint) {
this._positionStrategy.getPopoverInsertionPoint().after(this._host);
const customInsertionPoint = this._config.usePopover
? this._positionStrategy?.getPopoverInsertionPoint?.()
: null;

if (customInsertionPoint) {
customInsertionPoint.after(this._host);
} else {
this._previousHostParent?.appendChild(this._host);
}
Expand Down
8 changes: 6 additions & 2 deletions src/cdk/overlay/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,12 @@ export function createOverlayRef(injector: Injector, config?: OverlayConfig): Ov
host.classList.add('cdk-overlay-popover');
}

if (overlayConfig.usePopover && overlayConfig.positionStrategy?.getPopoverInsertionPoint) {
overlayConfig.positionStrategy.getPopoverInsertionPoint().after(host);
const customInsertionPoint = overlayConfig.usePopover
? overlayConfig.positionStrategy?.getPopoverInsertionPoint?.()
: null;

if (customInsertionPoint) {
customInsertionPoint.after(host);
} else {
overlayContainer.getContainerElement().appendChild(host);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2964,17 +2964,16 @@ describe('FlexibleConnectedPositionStrategy', () => {
originElement = createPositionedBlockElement();
document.body.appendChild(originElement);

positionStrategy = createFlexibleConnectedPositionStrategy(
injector,
originElement,
).withPositions([
{
overlayX: 'start',
overlayY: 'top',
originX: 'start',
originY: 'bottom',
},
]);
positionStrategy = createFlexibleConnectedPositionStrategy(injector, originElement)
.withPopoverLocation('inline')
.withPositions([
{
overlayX: 'start',
overlayY: 'top',
originX: 'start',
originY: 'bottom',
},
]);
});

afterEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export function createFlexibleConnectedPositionStrategy(
);
}

/** Supported locations in the DOM for connected overlays. */
export type FlexibleOverlayPopoverLocation = 'global' | 'inline';

/**
* A strategy for positioning overlays. Using this strategy, an overlay is given an
* implicit position relative some origin element. The relative position is defined in terms of
Expand Down Expand Up @@ -158,6 +161,9 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
/** Amount by which the overlay was pushed in each axis during the last time it was positioned. */
private _previousPushAmount: {x: number; y: number} | null;

/** Configures where in the DOM to insert the overlay when popovers are enabled. */
private _popoverLocation: FlexibleOverlayPopoverLocation = 'global';

/** Observable sequence of position changes. */
positionChanges: Observable<ConnectedOverlayPositionChange> = this._positionChanges;

Expand Down Expand Up @@ -511,8 +517,24 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
return this;
}

/**
* Determines where in the DOM the overlay will be rendered when popover mode is enabled.
* @param location Configures the location in the DOM. Supports the following values:
* - `global` - The default which inserts the overlay inside the overlay container.
* - `inline` - Inserts the overlay next to the trigger.
*/
withPopoverLocation(location: FlexibleOverlayPopoverLocation): this {
this._popoverLocation = location;
return this;
}

/** @docs-private */
getPopoverInsertionPoint(): Element {
getPopoverInsertionPoint(): Element | null {
// Return null so it falls back to inserting into the overlay container.
if (this._popoverLocation === 'global') {
return null;
}

const origin = this._origin;

if (origin instanceof ElementRef) {
Expand Down
46 changes: 0 additions & 46 deletions src/cdk/overlay/position/global-position-strategy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import {
OverlayRef,
createOverlayRef,
createGlobalPositionStrategy,
GlobalPositionStrategy,
OverlayContainer,
} from '../index';

describe('GlobalPositonStrategy', () => {
Expand Down Expand Up @@ -472,50 +470,6 @@ describe('GlobalPositonStrategy', () => {
expect(elementStyle.marginRight).toBe('');
expect(parentStyle.justifyContent).toBe('flex-end');
});

describe('DOM location', () => {
let positionStrategy: GlobalPositionStrategy;
let containerElement: HTMLElement;

beforeEach(() => {
containerElement = TestBed.inject(OverlayContainer).getContainerElement();
positionStrategy = createGlobalPositionStrategy(injector);
});

it('should place the overlay inside the overlay container by default', () => {
attachOverlay({positionStrategy, usePopover: false});
expect(containerElement.contains(overlayRef.hostElement)).toBe(true);
expect(overlayRef.hostElement.getAttribute('popover')).toBeFalsy();
});

it('should be able to opt into placing the overlay inside a popover element', () => {
if (!('showPopover' in document.body)) {
return;
}

attachOverlay({positionStrategy, usePopover: true});

expect(containerElement.contains(overlayRef.hostElement)).toBe(false);
expect(document.body.lastChild).toBe(overlayRef.hostElement);
expect(overlayRef.hostElement.getAttribute('popover')).toBe('manual');
});

it('should re-attach the popover at the end of the body', () => {
if (!('showPopover' in document.body)) {
return;
}

attachOverlay({positionStrategy, usePopover: true});
expect(document.body.lastChild).toBe(overlayRef.hostElement);

overlayRef.detach();
TestBed.inject(ApplicationRef).tick();
expect(overlayRef.hostElement.parentNode).toBeFalsy();

overlayRef.attach(portal);
expect(document.body.lastChild).toBe(overlayRef.hostElement);
});
});
});

@Component({
Expand Down
18 changes: 3 additions & 15 deletions src/cdk/overlay/position/global-position-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {DOCUMENT, Injector} from '@angular/core';
import {Injector} from '@angular/core';
import {OverlayRef} from '../overlay-ref';
import {PositionStrategy} from './position-strategy';

Expand All @@ -17,8 +17,8 @@ const wrapperClass = 'cdk-global-overlay-wrapper';
* Creates a global position strategy.
* @param injector Injector used to resolve dependencies for the strategy.
*/
export function createGlobalPositionStrategy(injector: Injector): GlobalPositionStrategy {
return new GlobalPositionStrategy(injector);
export function createGlobalPositionStrategy(_injector: Injector): GlobalPositionStrategy {
return new GlobalPositionStrategy();
}

/**
Expand All @@ -39,13 +39,6 @@ export class GlobalPositionStrategy implements PositionStrategy {
private _width = '';
private _height = '';
private _isDisposed = false;
private _document: Document;

constructor(injector?: Injector) {
// TODO(crisbeto): injector should be required, but some internal apps
// don't go through `createGlobalPositionStrategy` so they don't provide it.
this._document = injector?.get(DOCUMENT) || document;
}

attach(overlayRef: OverlayRef): void {
const config = overlayRef.getConfig();
Expand Down Expand Up @@ -274,9 +267,4 @@ export class GlobalPositionStrategy implements PositionStrategy {
this._overlayRef = null!;
this._isDisposed = true;
}

/** @docs-private */
getPopoverInsertionPoint(): Element {
return this._document.body.lastChild as Element;
}
}
2 changes: 1 addition & 1 deletion src/cdk/overlay/position/position-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ export interface PositionStrategy {
* Gets the element in the DOM after which to insert
* the overlay when it is rendered out as a popover.
*/
getPopoverInsertionPoint?(): Element;
getPopoverInsertionPoint?(): Element | null;
}
1 change: 1 addition & 0 deletions src/cdk/overlay/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export {
createGlobalPositionStrategy,
} from './position/global-position-strategy';
export {
FlexibleOverlayPopoverLocation,
ConnectedPosition,
FlexibleConnectedPositionStrategy,
FlexibleConnectedPositionStrategyOrigin,
Expand Down
4 changes: 3 additions & 1 deletion src/material/tooltip/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,8 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
.withTransformOriginOn(`.${this._cssClassPrefix}-tooltip`)
.withFlexibleDimensions(false)
.withViewportMargin(this._viewportMargin)
.withScrollableContainers(scrollableAncestors);
.withScrollableContainers(scrollableAncestors)
.withPopoverLocation('global');

strategy.positionChanges.pipe(takeUntil(this._destroyed)).subscribe(change => {
this._updateCurrentPositionClass(change.connectionPair);
Expand All @@ -528,6 +529,7 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
panelClass: this._overlayPanelClass ? [...this._overlayPanelClass, panelClass] : panelClass,
scrollStrategy: this._injector.get(MAT_TOOLTIP_SCROLL_STRATEGY)(),
disableAnimations: this._animationsDisabled,
usePopover: true,
});

this._updatePosition(this._overlayRef);
Expand Down
Loading