mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(overlay): centered flexible positioning not working in some browsers (#10701)
Currently using centering with the `FlexibleConnectedPositionStrategy` in flexible mode doesn't work in browsers that haven't implemented [the new absolute position behavior inside a flex container](https://developers.google.com/web/updates/2016/06/absolute-positioned-children) (e.g. iOS Safari), which means that the position will be thrown off completely. These changes rework the position strategy to use `position: static` and `justify-content`/`align-items`, rather than `position: absolute`, to push the overlay pane to the proper edge of the bounding box in flexible mode. As a result, support for having flexible dimensions in only one direction has been removed.
This commit is contained in:
committed by
tinayuangao
parent
b472330659
commit
dfc3b026e7
@@ -51,8 +51,9 @@ $backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
|
||||
|
||||
// A single overlay pane.
|
||||
.cdk-overlay-pane {
|
||||
// Note: it's important for this one to start off `absolute`,
|
||||
// in order for us to be able to measure it correctly.
|
||||
position: absolute;
|
||||
|
||||
pointer-events: auto;
|
||||
box-sizing: border-box;
|
||||
z-index: $cdk-z-index-overlay;
|
||||
@@ -113,8 +114,10 @@ $backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
|
||||
// When *not* centering, a top/left/bottom/right will be set which overrides the normal
|
||||
// flex layout.
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
// We use the `column` direction here to avoid some flexbox issues in Edge
|
||||
// when using the "grow after open" options.
|
||||
flex-direction: column;
|
||||
|
||||
// Add some dimensions so the element has an `innerText` which some people depend on in tests.
|
||||
min-width: 1px;
|
||||
|
||||
@@ -289,8 +289,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
|
||||
// the same way as the old ConnectedPositionStrategy and to avoid breaking changes.
|
||||
// TODO(crisbeto): make these on by default and add inputs for them
|
||||
// next time we do breaking changes.
|
||||
.withFlexibleHeight(false)
|
||||
.withFlexibleWidth(false)
|
||||
.withFlexibleDimensions(false)
|
||||
.withPush(false)
|
||||
.withGrowAfterOpen(false)
|
||||
.withLockedPosition(this.lockPosition);
|
||||
|
||||
@@ -70,8 +70,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
|
||||
// proxy all of the API calls.
|
||||
this._positionStrategy =
|
||||
new FlexibleConnectedPositionStrategy(connectedTo, viewportRuler, document)
|
||||
.withFlexibleHeight(false)
|
||||
.withFlexibleWidth(false)
|
||||
.withFlexibleDimensions(false)
|
||||
.withPush(false)
|
||||
.withViewportMargin(0);
|
||||
|
||||
|
||||
@@ -120,8 +120,7 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
document.body.appendChild(originElement);
|
||||
positionStrategy = overlay.position()
|
||||
.flexibleConnectedTo(new ElementRef(originElement))
|
||||
.withFlexibleHeight(false)
|
||||
.withFlexibleWidth(false)
|
||||
.withFlexibleDimensions(false)
|
||||
.withPush(false);
|
||||
});
|
||||
|
||||
@@ -865,8 +864,7 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
document.body.appendChild(originElement);
|
||||
positionStrategy = overlay.position()
|
||||
.flexibleConnectedTo(new ElementRef(originElement))
|
||||
.withFlexibleHeight(false)
|
||||
.withFlexibleWidth(false)
|
||||
.withFlexibleDimensions(false)
|
||||
.withPush();
|
||||
});
|
||||
|
||||
@@ -1003,8 +1001,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
it('should align the overlay to `flex-start` when the content is flowing to the right', () => {
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'start',
|
||||
@@ -1014,13 +1012,13 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
expect(overlayRef.overlayElement.style.justifyContent).toBe('flex-start');
|
||||
expect(overlayRef.hostElement.style.alignItems).toBe('flex-start');
|
||||
});
|
||||
|
||||
it('should align the overlay to `flex-end` when the content is flowing to the left', () => {
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'end',
|
||||
@@ -1030,13 +1028,13 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
expect(overlayRef.overlayElement.style.justifyContent).toBe('flex-end');
|
||||
expect(overlayRef.hostElement.style.alignItems).toBe('flex-end');
|
||||
});
|
||||
|
||||
it('should align the overlay to `center` when the content is centered', () => {
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'center',
|
||||
@@ -1046,48 +1044,48 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
expect(overlayRef.overlayElement.style.justifyContent).toBe('center');
|
||||
expect(overlayRef.hostElement.style.alignItems).toBe('center');
|
||||
});
|
||||
|
||||
// TODO(crisbeto): investigate failure in older Safari
|
||||
// it('should support offsets when centering', () => {
|
||||
// originElement.style.top = '200px';
|
||||
// originElement.style.left = '200px';
|
||||
it('should support offsets when centering', () => {
|
||||
originElement.style.top = '200px';
|
||||
originElement.style.left = '200px';
|
||||
|
||||
// positionStrategy
|
||||
// .withFlexibleWidth()
|
||||
// .withFlexibleHeight()
|
||||
// .withPositions([{
|
||||
// overlayY: 'center',
|
||||
// overlayX: 'center',
|
||||
// originY: 'center',
|
||||
// originX: 'center',
|
||||
// offsetY: 20,
|
||||
// offsetX: -15
|
||||
// }]);
|
||||
positionStrategy
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'center',
|
||||
overlayX: 'center',
|
||||
originY: 'center',
|
||||
originX: 'center',
|
||||
offsetY: 20,
|
||||
offsetX: -15
|
||||
}]);
|
||||
|
||||
// attachOverlay({positionStrategy});
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
// const originRect = originElement.getBoundingClientRect();
|
||||
// const originCenterY = originRect.top + (ORIGIN_HEIGHT / 2);
|
||||
// const originCenterX = originRect.left + (ORIGIN_WIDTH / 2);
|
||||
const originRect = originElement.getBoundingClientRect();
|
||||
const originCenterX = originRect.left + (DEFAULT_WIDTH / 2);
|
||||
const originCenterY = originRect.top + (DEFAULT_HEIGHT / 2);
|
||||
|
||||
// const overlayRect = overlayRef.overlayElement.getBoundingClientRect();
|
||||
// const overlayCenterY = overlayRect.top + (OVERLAY_HEIGHT / 2);
|
||||
// const overlayCenterX = overlayRect.left + (OVERLAY_WIDTH / 2);
|
||||
const overlayRect = overlayRef.overlayElement.getBoundingClientRect();
|
||||
const overlayCenterY = overlayRect.top + (OVERLAY_HEIGHT / 2);
|
||||
const overlayCenterX = overlayRect.left + (OVERLAY_WIDTH / 2);
|
||||
|
||||
// expect(overlayRef.overlayElement.style.transform)
|
||||
// .toBe('translateX(-15px) translateY(20px)');
|
||||
// expect(Math.floor(overlayCenterY)).toBe(Math.floor(originCenterY) + 20);
|
||||
// expect(Math.floor(overlayCenterX)).toBe(Math.floor(originCenterX) - 15);
|
||||
// });
|
||||
expect(overlayRef.overlayElement.style.transform)
|
||||
.toBe('translateX(-15px) translateY(20px)');
|
||||
expect(Math.floor(overlayCenterY)).toBe(Math.floor(originCenterY) + 20);
|
||||
expect(Math.floor(overlayCenterX)).toBe(Math.floor(originCenterX) - 15);
|
||||
});
|
||||
|
||||
it('should become scrollable when it hits the viewport edge with a flexible height', () => {
|
||||
originElement.style.left = '200px';
|
||||
originElement.style.bottom = `${OVERLAY_HEIGHT - 10}px`;
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'start',
|
||||
@@ -1107,7 +1105,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.right = '-20px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'start',
|
||||
@@ -1127,7 +1126,7 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.bottom = `${OVERLAY_HEIGHT - 10}px`;
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'start',
|
||||
@@ -1149,7 +1148,7 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.right = '-20px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleDimensions()
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'start',
|
||||
@@ -1171,7 +1170,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.right = '25px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([
|
||||
{
|
||||
originX: 'end',
|
||||
@@ -1202,7 +1202,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.bottom = `${OVERLAY_HEIGHT - 10}px`;
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withGrowAfterOpen()
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
@@ -1239,7 +1240,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
window.scroll(0, 50);
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'bottom',
|
||||
overlayX: 'start',
|
||||
@@ -1257,14 +1259,15 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
window.scroll(0, 0);
|
||||
document.body.removeChild(veryLargeElement);
|
||||
});
|
||||
|
||||
it('should set the proper styles when the `bottom` value is exactly zero', () => {
|
||||
originElement.style.position = 'fixed';
|
||||
originElement.style.bottom = '0';
|
||||
originElement.style.left = '200px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'bottom',
|
||||
overlayX: 'start',
|
||||
@@ -1289,8 +1292,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.left = '200px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'start',
|
||||
@@ -1315,8 +1318,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.top = '200px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'start',
|
||||
@@ -1341,8 +1344,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.top = '200px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleWidth()
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
overlayY: 'top',
|
||||
overlayX: 'end',
|
||||
@@ -1368,7 +1371,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
originElement.style.right = '200px';
|
||||
|
||||
positionStrategy
|
||||
.withFlexibleHeight()
|
||||
.withFlexibleDimensions()
|
||||
.withPush(false)
|
||||
.withViewportMargin(viewportMargin)
|
||||
.withPositions([
|
||||
{
|
||||
@@ -1408,6 +1412,7 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
// Create a strategy with knowledge of the scrollable container
|
||||
const strategy = overlay.position()
|
||||
.flexibleConnectedTo(new ElementRef(originElement))
|
||||
.withPush(false)
|
||||
.withPositions([{
|
||||
originX: 'start',
|
||||
originY: 'bottom',
|
||||
@@ -1503,8 +1508,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
expect(overlayRef.overlayElement.style.left).toBeTruthy();
|
||||
expect(overlayRef.overlayElement.style.right).toBeFalsy();
|
||||
expect(overlayRef.hostElement.style.left).toBeTruthy();
|
||||
expect(overlayRef.hostElement.style.right).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should use `right` when positioning an element at the end', () => {
|
||||
@@ -1517,8 +1522,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
expect(overlayRef.overlayElement.style.right).toBeTruthy();
|
||||
expect(overlayRef.overlayElement.style.left).toBeFalsy();
|
||||
expect(overlayRef.hostElement.style.right).toBeTruthy();
|
||||
expect(overlayRef.hostElement.style.left).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1537,8 +1542,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
direction: 'rtl'
|
||||
});
|
||||
|
||||
expect(overlayRef.overlayElement.style.right).toBeTruthy();
|
||||
expect(overlayRef.overlayElement.style.left).toBeFalsy();
|
||||
expect(overlayRef.hostElement.style.right).toBeTruthy();
|
||||
expect(overlayRef.hostElement.style.left).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should use `left` when positioning an element at the end', () => {
|
||||
@@ -1551,8 +1556,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy, direction: 'rtl'});
|
||||
|
||||
expect(overlayRef.overlayElement.style.left).toBeTruthy();
|
||||
expect(overlayRef.overlayElement.style.right).toBeFalsy();
|
||||
expect(overlayRef.hostElement.style.left).toBeTruthy();
|
||||
expect(overlayRef.hostElement.style.right).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1567,8 +1572,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
expect(overlayRef.overlayElement.style.top).toBeTruthy();
|
||||
expect(overlayRef.overlayElement.style.bottom).toBeFalsy();
|
||||
expect(overlayRef.hostElement.style.top).toBeTruthy();
|
||||
expect(overlayRef.hostElement.style.bottom).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should use `bottom` when positioning at element along the bottom', () => {
|
||||
@@ -1581,8 +1586,8 @@ describe('FlexibleConnectedPositionStrategy', () => {
|
||||
|
||||
attachOverlay({positionStrategy});
|
||||
|
||||
expect(overlayRef.overlayElement.style.bottom).toBeTruthy();
|
||||
expect(overlayRef.overlayElement.style.top).toBeFalsy();
|
||||
expect(overlayRef.hostElement.style.bottom).toBeTruthy();
|
||||
expect(overlayRef.hostElement.style.top).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -51,11 +51,8 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
/** Whether the overlay can grow via flexible width/height after the initial open. */
|
||||
private _growAfterOpen = false;
|
||||
|
||||
/** Whether the overlay's height can be constrained to fit within the viewport. */
|
||||
private _hasFlexibleHeight = true;
|
||||
|
||||
/** Whether the overlay's width can be constrained to fit within the viewport. */
|
||||
private _hasFlexibleWidth = true;
|
||||
/** Whether the overlay's width and height can be constrained to fit within the viewport. */
|
||||
private _hasFlexibleDimensions = true;
|
||||
|
||||
/** Whether the overlay position is locked. */
|
||||
private _positionLocked = false;
|
||||
@@ -167,6 +164,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
return;
|
||||
}
|
||||
|
||||
this._resetOverlayElementStyles();
|
||||
this._resetBoundingBoxStyles();
|
||||
|
||||
// We need the bounding rects for the origin and the overlay to determine how to position
|
||||
@@ -329,15 +327,9 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets whether the overlay's height can be constrained to fit within the viewport. */
|
||||
withFlexibleHeight(flexibleHeight = true): this {
|
||||
this._hasFlexibleHeight = flexibleHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets whether the overlay's width can be constrained to fit within the viewport. */
|
||||
withFlexibleWidth(flexibleWidth = true): this {
|
||||
this._hasFlexibleWidth = flexibleWidth;
|
||||
/** Sets whether the overlay's width and height can be constrained to fit within the viewport. */
|
||||
withFlexibleDimensions(flexibleDimensions = true): this {
|
||||
this._hasFlexibleDimensions = flexibleDimensions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -494,16 +486,16 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
* @param viewport The geometry of the viewport.
|
||||
*/
|
||||
private _canFitWithFlexibleDimensions(fit: OverlayFit, point: Point, viewport: ClientRect) {
|
||||
if (this._hasFlexibleWidth || this._hasFlexibleWidth) {
|
||||
if (this._hasFlexibleDimensions) {
|
||||
const availableHeight = viewport.bottom - point.y;
|
||||
const availableWidth = viewport.right - point.x;
|
||||
const minHeight = this._overlayRef.getConfig().minHeight || 0;
|
||||
const minWidth = this._overlayRef.getConfig().minWidth || 0;
|
||||
const minHeight = this._overlayRef.getConfig().minHeight;
|
||||
const minWidth = this._overlayRef.getConfig().minWidth;
|
||||
|
||||
const verticalFit = fit.fitsInViewportVertically ||
|
||||
(this._hasFlexibleHeight && minHeight <= availableHeight);
|
||||
(minHeight != null && minHeight <= availableHeight);
|
||||
const horizontalFit = fit.fitsInViewportHorizontally ||
|
||||
(this._hasFlexibleWidth && minWidth <= availableWidth);
|
||||
(minWidth != null && minWidth <= availableWidth);
|
||||
|
||||
return verticalFit && horizontalFit;
|
||||
}
|
||||
@@ -664,34 +656,43 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
|
||||
const styles = {} as CSSStyleDeclaration;
|
||||
|
||||
if (!this._hasFlexibleHeight || this._isPushed) {
|
||||
styles.top = '0';
|
||||
styles.bottom = '';
|
||||
styles.height = '100%';
|
||||
if (this._hasExactPosition()) {
|
||||
styles.top = styles.left = '0';
|
||||
styles.bottom = styles.right = '';
|
||||
styles.width = styles.height = '100%';
|
||||
} else {
|
||||
const maxHeight = this._overlayRef.getConfig().maxHeight;
|
||||
const maxWidth = this._overlayRef.getConfig().maxWidth;
|
||||
|
||||
styles.height = coerceCssPixelValue(boundingBoxRect.height);
|
||||
styles.top = coerceCssPixelValue(boundingBoxRect.top);
|
||||
styles.bottom = coerceCssPixelValue(boundingBoxRect.bottom);
|
||||
styles.height = coerceCssPixelValue(boundingBoxRect.height);
|
||||
}
|
||||
|
||||
if (!this._hasFlexibleWidth || this._isPushed) {
|
||||
styles.left = '0';
|
||||
styles.right = '';
|
||||
styles.width = '100%';
|
||||
} else {
|
||||
styles.width = coerceCssPixelValue(boundingBoxRect.width);
|
||||
styles.left = coerceCssPixelValue(boundingBoxRect.left);
|
||||
styles.right = coerceCssPixelValue(boundingBoxRect.right);
|
||||
styles.width = coerceCssPixelValue(boundingBoxRect.width);
|
||||
}
|
||||
|
||||
const maxHeight = this._overlayRef.getConfig().maxHeight;
|
||||
if (maxHeight && this._hasFlexibleHeight) {
|
||||
styles.maxHeight = coerceCssPixelValue(maxHeight);
|
||||
}
|
||||
// Push the pane content towards the proper direction.
|
||||
if (position.overlayX === 'center') {
|
||||
styles.alignItems = 'center';
|
||||
} else if (this._isRtl()) {
|
||||
styles.alignItems = position.overlayX === 'end' ? 'flex-start' : 'flex-end';
|
||||
} else {
|
||||
styles.alignItems = position.overlayX === 'end' ? 'flex-end' : 'flex-start';
|
||||
}
|
||||
|
||||
const maxWidth = this._overlayRef.getConfig().maxWidth;
|
||||
if (maxWidth && this._hasFlexibleWidth) {
|
||||
styles.maxWidth = coerceCssPixelValue(maxWidth);
|
||||
if (position.overlayY === 'center') {
|
||||
styles.justifyContent = 'center';
|
||||
} else {
|
||||
styles.justifyContent = position.overlayY === 'bottom' ? 'flex-end' : 'flex-start';
|
||||
}
|
||||
|
||||
if (maxHeight) {
|
||||
styles.maxHeight = coerceCssPixelValue(maxHeight);
|
||||
}
|
||||
|
||||
if (maxWidth) {
|
||||
styles.maxWidth = coerceCssPixelValue(maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
this._lastBoundingBoxSize = boundingBoxRect;
|
||||
@@ -713,36 +714,26 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
} as CSSStyleDeclaration);
|
||||
}
|
||||
|
||||
/** Sets positioning styles to the overlay element. */
|
||||
private _setOverlayElementStyles(originPoint: Point, position: ConnectedPosition): void {
|
||||
// Reset styles from any previous positioning.
|
||||
const styles = {
|
||||
/** Resets the styles for the overlay pane so that a new positioning can be computed. */
|
||||
private _resetOverlayElementStyles() {
|
||||
extendStyles(this._pane.style, {
|
||||
top: '',
|
||||
left: '',
|
||||
bottom: '',
|
||||
right: '',
|
||||
} as CSSStyleDeclaration;
|
||||
position: '',
|
||||
} as CSSStyleDeclaration);
|
||||
}
|
||||
|
||||
// Align the overlay panel to the appropriate edge of the
|
||||
// size-constraining container unless using a 'center' position.
|
||||
if (this._hasFlexibleWidth && position.overlayX !== 'center' && !this._isPushed) {
|
||||
if (this._isRtl()) {
|
||||
styles[position.overlayX === 'end' ? 'left' : 'right'] = '0';
|
||||
} else {
|
||||
styles[position.overlayX === 'end' ? 'right' : 'left'] = '0';
|
||||
}
|
||||
}
|
||||
/** Sets positioning styles to the overlay element. */
|
||||
private _setOverlayElementStyles(originPoint: Point, position: ConnectedPosition): void {
|
||||
const styles = {} as CSSStyleDeclaration;
|
||||
|
||||
if (this._hasFlexibleHeight && position.overlayY !== 'center' && !this._isPushed) {
|
||||
styles[position.overlayY === 'bottom' ? 'bottom' : 'top'] = '0';
|
||||
}
|
||||
|
||||
if (!this._hasFlexibleHeight || this._isPushed) {
|
||||
if (this._hasExactPosition()) {
|
||||
extendStyles(styles, this._getExactOverlayY(position, originPoint));
|
||||
}
|
||||
|
||||
if (!this._hasFlexibleWidth || this._isPushed) {
|
||||
extendStyles(styles, this._getExactOverlayX(position, originPoint));
|
||||
} else {
|
||||
styles.position = 'static';
|
||||
}
|
||||
|
||||
// Use a transform to apply the offsets. We do this because the `center` positions rely on
|
||||
@@ -750,12 +741,12 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
// off the position. We also can't use margins, because they won't have an effect in some
|
||||
// cases where the element doesn't have anything to "push off of". Finally, this works
|
||||
// better both with flexible and non-flexible positioning.
|
||||
let transformString = ' ';
|
||||
let transformString = '';
|
||||
let offsetX = this._getOffset(position, 'x');
|
||||
let offsetY = this._getOffset(position, 'y');
|
||||
|
||||
if (offsetX) {
|
||||
transformString += `translateX(${offsetX}px)`;
|
||||
transformString += `translateX(${offsetX}px) `;
|
||||
}
|
||||
|
||||
if (offsetY) {
|
||||
@@ -767,21 +758,14 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
// If a maxWidth or maxHeight is specified on the overlay, we remove them. We do this because
|
||||
// we need these values to both be set to "100%" for the automatic flexible sizing to work.
|
||||
// The maxHeight and maxWidth are set on the boundingBox in order to enforce the constraint.
|
||||
if (this._hasFlexibleHeight && this._overlayRef.getConfig().maxHeight) {
|
||||
if (this._hasFlexibleDimensions && this._overlayRef.getConfig().maxHeight) {
|
||||
styles.maxHeight = '';
|
||||
}
|
||||
|
||||
if (this._hasFlexibleWidth && this._overlayRef.getConfig().maxWidth) {
|
||||
if (this._hasFlexibleDimensions && this._overlayRef.getConfig().maxWidth) {
|
||||
styles.maxWidth = '';
|
||||
}
|
||||
|
||||
// Push the pane content towards the proper direction.
|
||||
if (position.overlayX === 'center') {
|
||||
styles.justifyContent = 'center';
|
||||
} else {
|
||||
styles.justifyContent = position.overlayX === 'end' ? 'flex-end' : 'flex-start';
|
||||
}
|
||||
|
||||
extendStyles(this._pane.style, styles);
|
||||
}
|
||||
|
||||
@@ -902,6 +886,11 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
|
||||
return this._overlayRef.getConfig().direction === 'rtl';
|
||||
}
|
||||
|
||||
/** Determines whether the overlay uses exact or flexible positioning. */
|
||||
private _hasExactPosition() {
|
||||
return !this._hasFlexibleDimensions || this._isPushed;
|
||||
}
|
||||
|
||||
/** Retrieves the offset of a position along the x or y axis. */
|
||||
private _getOffset(position: ConnectedPosition, axis: 'x' | 'y') {
|
||||
if (axis === 'x') {
|
||||
|
||||
@@ -49,8 +49,7 @@ export class ConnectedOverlayDemo {
|
||||
openWithConfig() {
|
||||
const positionStrategy = this.overlay.position()
|
||||
.flexibleConnectedTo(this._overlayOrigin.elementRef)
|
||||
.withFlexibleHeight(this.isFlexible)
|
||||
.withFlexibleWidth(this.isFlexible)
|
||||
.withFlexibleDimensions(this.isFlexible)
|
||||
.withPush(this.canPush)
|
||||
.withViewportMargin(10)
|
||||
.withGrowAfterOpen(true)
|
||||
|
||||
@@ -531,8 +531,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
private _getOverlayPosition(): PositionStrategy {
|
||||
this._positionStrategy = this._overlay.position()
|
||||
.flexibleConnectedTo(this._getConnectedElement())
|
||||
.withFlexibleHeight(false)
|
||||
.withFlexibleWidth(false)
|
||||
.withFlexibleDimensions(false)
|
||||
.withPush(false)
|
||||
.withPositions([
|
||||
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top'},
|
||||
|
||||
@@ -484,8 +484,7 @@ export class MatDatepicker<D> implements OnDestroy, CanColor {
|
||||
private _createPopupPositionStrategy(): PositionStrategy {
|
||||
return this._overlay.position()
|
||||
.flexibleConnectedTo(this._datepickerInput.getPopupConnectionElementRef())
|
||||
.withFlexibleHeight(false)
|
||||
.withFlexibleWidth(false)
|
||||
.withFlexibleDimensions(false)
|
||||
.withViewportMargin(8)
|
||||
.withPush(false)
|
||||
.withPositions([
|
||||
|
||||
@@ -482,7 +482,7 @@ describe('MatMenu', () => {
|
||||
// Push trigger to the right side of viewport, so it doesn't have space to open
|
||||
// in its default "after" position on the right side.
|
||||
trigger.style.position = 'fixed';
|
||||
trigger.style.right = '-50px';
|
||||
trigger.style.right = '0';
|
||||
trigger.style.top = '200px';
|
||||
|
||||
fixture.componentInstance.trigger.openMenu();
|
||||
@@ -541,8 +541,8 @@ describe('MatMenu', () => {
|
||||
// push trigger to the bottom, right part of viewport, so it doesn't have space to open
|
||||
// in its default "after below" position.
|
||||
trigger.style.position = 'fixed';
|
||||
trigger.style.right = '-50px';
|
||||
trigger.style.bottom = '65px';
|
||||
trigger.style.right = '0';
|
||||
trigger.style.bottom = '0';
|
||||
|
||||
fixture.componentInstance.trigger.openMenu();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -309,8 +309,7 @@ export class MatTooltip implements OnDestroy {
|
||||
// Create connected position strategy that listens for scroll events to reposition.
|
||||
const strategy = this._overlay.position()
|
||||
.flexibleConnectedTo(this._elementRef)
|
||||
.withFlexibleHeight(false)
|
||||
.withFlexibleWidth(false)
|
||||
.withFlexibleDimensions(false)
|
||||
.withViewportMargin(8)
|
||||
.withPositions([
|
||||
{...origin.main, ...overlay.main},
|
||||
|
||||
Reference in New Issue
Block a user