Skip to content

Commit bcff93e

Browse files
crisbetojosephperrott
authored andcommitted
feat(overlay): add the ability to set the default offsets on FlexibleConnectedPositionStrategy (#10555)
1 parent b47bfaf commit bcff93e

File tree

3 files changed

+120
-21
lines changed

3 files changed

+120
-21
lines changed

src/cdk/overlay/position/connected-position-strategy.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
174174
* @param offset New offset in the X axis.
175175
*/
176176
withOffsetX(offset: number): this {
177-
this._preferredPositions.forEach(position => {
178-
if (position.offsetX == null) {
179-
position.offsetX = offset;
180-
}
181-
});
182-
177+
this._positionStrategy.withDefaultOffsetX(offset);
183178
return this;
184179
}
185180

@@ -188,12 +183,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
188183
* @param offset New offset in the Y axis.
189184
*/
190185
withOffsetY(offset: number): this {
191-
this._preferredPositions.forEach(position => {
192-
if (position.offsetY == null) {
193-
position.offsetY = offset;
194-
}
195-
});
196-
186+
this._positionStrategy.withDefaultOffsetY(offset);
197187
return this;
198188
}
199189

src/cdk/overlay/position/flexible-connected-position-strategy.spec.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,41 @@ describe('FlexibleConnectedPositionStrategy', () => {
385385
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 10));
386386
});
387387

388+
it('should be able to set the default x offset', () => {
389+
const originRect = originElement.getBoundingClientRect();
390+
391+
positionStrategy.withDefaultOffsetX(20).withPositions([{
392+
originX: 'start',
393+
originY: 'top',
394+
overlayX: 'start',
395+
overlayY: 'top',
396+
}]);
397+
398+
attachOverlay({positionStrategy});
399+
400+
const overlayRect = overlayRef.overlayElement.getBoundingClientRect();
401+
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top));
402+
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 20));
403+
});
404+
405+
it('should have the position offset x take precedence over the default offset x', () => {
406+
const originRect = originElement.getBoundingClientRect();
407+
408+
positionStrategy.withDefaultOffsetX(20).withPositions([{
409+
originX: 'start',
410+
originY: 'top',
411+
overlayX: 'start',
412+
overlayY: 'top',
413+
offsetX: 10
414+
}]);
415+
416+
attachOverlay({positionStrategy});
417+
418+
const overlayRect = overlayRef.overlayElement.getBoundingClientRect();
419+
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top));
420+
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 10));
421+
});
422+
388423
it('should position a panel with the y offset provided', () => {
389424
const originRect = originElement.getBoundingClientRect();
390425

@@ -403,6 +438,41 @@ describe('FlexibleConnectedPositionStrategy', () => {
403438
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left));
404439
});
405440

441+
it('should be able to set the default y offset', () => {
442+
const originRect = originElement.getBoundingClientRect();
443+
444+
positionStrategy.withDefaultOffsetY(60).withPositions([{
445+
originX: 'start',
446+
originY: 'top',
447+
overlayX: 'start',
448+
overlayY: 'top',
449+
}]);
450+
451+
attachOverlay({positionStrategy});
452+
453+
const overlayRect = overlayRef.overlayElement.getBoundingClientRect();
454+
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top + 60));
455+
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left));
456+
});
457+
458+
it('should have the position offset y take precedence over the default offset y', () => {
459+
const originRect = originElement.getBoundingClientRect();
460+
461+
positionStrategy.withDefaultOffsetY(60).withPositions([{
462+
originX: 'start',
463+
originY: 'top',
464+
overlayX: 'start',
465+
overlayY: 'top',
466+
offsetY: 50
467+
}]);
468+
469+
attachOverlay({positionStrategy});
470+
471+
const overlayRect = overlayRef.overlayElement.getBoundingClientRect();
472+
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top + 50));
473+
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left));
474+
});
475+
406476
it('should allow for the fallback positions to specify their own offsets', () => {
407477
originElement.style.bottom = '0';
408478
originElement.style.left = '50%';

src/cdk/overlay/position/flexible-connected-position-strategy.ts

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
6969
private _viewportRect: ClientRect;
7070

7171
/** Amount of space that must be maintained between the overlay and the edge of the viewport. */
72-
private _viewportMargin: number = 0;
72+
private _viewportMargin = 0;
7373

7474
/** The Scrollable containers used to check scrollable view properties on position change. */
7575
private scrollables: CdkScrollable[] = [];
@@ -101,6 +101,12 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
101101
/** Subscription to viewport size changes. */
102102
private _resizeSubscription = Subscription.EMPTY;
103103

104+
/** Default offset for the overlay along the x axis. */
105+
private _offsetX = 0;
106+
107+
/** Default offset for the overlay along the y axis. */
108+
private _offsetY = 0;
109+
104110
/** Observable sequence of position changes. */
105111
positionChanges: Observable<ConnectedOverlayPositionChange> =
106112
this._positionChanges.asObservable();
@@ -366,6 +372,24 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
366372
return this;
367373
}
368374

375+
/**
376+
* Sets the default offset for the overlay's connection point on the x-axis.
377+
* @param offset New offset in the X axis.
378+
*/
379+
withDefaultOffsetX(offset: number): this {
380+
this._offsetX = offset;
381+
return this;
382+
}
383+
384+
/**
385+
* Sets the default offset for the overlay's connection point on the y-axis.
386+
* @param offset New offset in the Y axis.
387+
*/
388+
withDefaultOffsetY(offset: number): this {
389+
this._offsetY = offset;
390+
return this;
391+
}
392+
369393
/**
370394
* Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
371395
*/
@@ -431,14 +455,16 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
431455
position: ConnectedPosition): OverlayFit {
432456

433457
let {x, y} = point;
458+
let offsetX = this._getOffset(position, 'x');
459+
let offsetY = this._getOffset(position, 'y');
434460

435461
// Account for the offsets since they could push the overlay out of the viewport.
436-
if (position.offsetX) {
437-
x += position.offsetX;
462+
if (offsetX) {
463+
x += offsetX;
438464
}
439465

440-
if (position.offsetY) {
441-
y += position.offsetY;
466+
if (offsetY) {
467+
y += offsetY;
442468
}
443469

444470
// How much the overlay would overflow at this position, on each side.
@@ -721,13 +747,15 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
721747
// cases where the element doesn't have anything to "push off of". Finally, this works
722748
// better both with flexible and non-flexible positioning.
723749
let transformString = ' ';
750+
let offsetX = this._getOffset(position, 'x');
751+
let offsetY = this._getOffset(position, 'y');
724752

725-
if (position.offsetX) {
726-
transformString += `translateX(${position.offsetX}px)`;
753+
if (offsetX) {
754+
transformString += `translateX(${offsetX}px)`;
727755
}
728756

729-
if (position.offsetY) {
730-
transformString += `translateY(${position.offsetY}px)`;
757+
if (offsetY) {
758+
transformString += `translateY(${offsetY}px)`;
731759
}
732760

733761
styles.transform = transformString.trim();
@@ -869,6 +897,17 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
869897
private _isRtl() {
870898
return this._overlayRef.getConfig().direction === 'rtl';
871899
}
900+
901+
/** Retrieves the offset of a position along the x or y axis. */
902+
private _getOffset(position: ConnectedPosition, axis: 'x' | 'y') {
903+
if (axis === 'x') {
904+
// We don't do something like `position['offset' + axis]` in
905+
// order to avoid breking minifiers that rename properties.
906+
return position.offsetX == null ? this._offsetX : position.offsetX;
907+
}
908+
909+
return position.offsetY == null ? this._offsetY : position.offsetY;
910+
}
872911
}
873912

874913
/** A simple (x, y) coordinate. */

0 commit comments

Comments
 (0)