Skip to content

Commit ce01163

Browse files
committed
feat(overlay): add placement and offset
1 parent 200e46b commit ce01163

File tree

5 files changed

+241
-8
lines changed

5 files changed

+241
-8
lines changed

projects/igniteui-angular/src/lib/services/overlay/position/base-fit-position-strategy.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,26 @@ export abstract class BaseFitPositionStrategy extends ConnectedPositioningStrate
4242
* @param connectedFit connectedFit to update
4343
*/
4444
protected updateViewPortFit(connectedFit: ConnectedFit) {
45+
const horizontalOffset = Util.getHorizontalOffset(connectedFit, this.settings);
4546
connectedFit.left = this.calculateLeft(
4647
connectedFit.targetRect,
4748
connectedFit.contentElementRect,
4849
this.settings.horizontalStartPoint,
4950
this.settings.horizontalDirection,
50-
connectedFit.horizontalOffset ? connectedFit.horizontalOffset : 0);
51+
horizontalOffset);
5152
connectedFit.right = connectedFit.left + connectedFit.contentElementRect.width;
5253
connectedFit.fitHorizontal = {
5354
back: Math.round(connectedFit.left),
5455
forward: Math.round(connectedFit.viewPortRect.width - connectedFit.right)
5556
};
5657

58+
const verticalOffset = Util.getVerticalOffset(connectedFit, this.settings);
5759
connectedFit.top = this.calculateTop(
5860
connectedFit.targetRect,
5961
connectedFit.contentElementRect,
6062
this.settings.verticalStartPoint,
6163
this.settings.verticalDirection,
62-
connectedFit.verticalOffset ? connectedFit.verticalOffset : 0);
64+
verticalOffset);
6365
connectedFit.bottom = connectedFit.top + connectedFit.contentElementRect.height;
6466
connectedFit.fitVertical = {
6567
back: Math.round(connectedFit.top),

projects/igniteui-angular/src/lib/services/overlay/position/connected-positioning-strategy.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,17 @@ export class ConnectedPositioningStrategy implements IPositionStrategy {
2727
verticalStartPoint: VerticalAlignment.Bottom,
2828
openAnimation: scaleInVerTop,
2929
closeAnimation: scaleOutVerTop,
30-
minSize: { width: 0, height: 0 }
30+
minSize: { width: 0, height: 0 },
31+
offset: 0
3132
};
3233

3334
constructor(settings?: PositionSettings) {
35+
36+
if (Util.canUsePlacement(settings)) {
37+
const placementPositionSettings = Util.getPositionSettingsByPlacement(settings.placement);
38+
settings = Object.assign({}, settings, placementPositionSettings);
39+
}
40+
3441
this.settings = Object.assign({}, this._defaultSettings, settings);
3542
}
3643

@@ -81,8 +88,9 @@ export class ConnectedPositioningStrategy implements IPositionStrategy {
8188
* @param elementRect Bounding rectangle of the element
8289
*/
8390
protected setStyle(element: HTMLElement, targetRect: Partial<DOMRect>, elementRect: Partial<DOMRect>, connectedFit: ConnectedFit) {
84-
const horizontalOffset = connectedFit.horizontalOffset ? connectedFit.horizontalOffset : 0;
85-
const verticalOffset = connectedFit.verticalOffset ? connectedFit.verticalOffset : 0;
91+
const horizontalOffset = Util.getHorizontalOffset(connectedFit, this.settings);
92+
const verticalOffset = Util.getVerticalOffset(connectedFit, this.settings);
93+
8694
const startPoint: Point = {
8795
x: targetRect.right + targetRect.width * this.settings.horizontalStartPoint + horizontalOffset,
8896
y: targetRect.bottom + targetRect.height * this.settings.verticalStartPoint + verticalOffset

projects/igniteui-angular/src/lib/services/overlay/position/global-position-strategy.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { fadeIn, fadeOut } from 'igniteui-angular/animations';
2-
import { HorizontalAlignment, PositionSettings, Util, VerticalAlignment } from './../utilities';
2+
import { HorizontalAlignment, Placement, PositionSettings, Util, VerticalAlignment } from './../utilities';
33
import { IPositionStrategy } from './IPositionStrategy';
44

55
/**
@@ -23,6 +23,16 @@ export class GlobalPositionStrategy implements IPositionStrategy {
2323
};
2424

2525
constructor(settings?: PositionSettings) {
26+
27+
if (Util.canUsePlacement(settings)) {
28+
const placement = this.shouldFlip(settings.placement)
29+
? this.getOppositePlacement(settings.placement)
30+
: settings.placement;
31+
32+
const placementPositionSettings = Util.getPositionSettingsByPlacement(placement);
33+
settings = Object.assign({}, settings, placementPositionSettings);
34+
}
35+
2636
this.settings = Object.assign({}, this._defaultSettings, settings);
2737
}
2838

@@ -83,5 +93,24 @@ export class GlobalPositionStrategy implements IPositionStrategy {
8393
break;
8494
}
8595
}
86-
}
8796

97+
private shouldFlip(placement: Placement): boolean {
98+
const endStartPositionsRegExp = /^(?:top|bottom|right|left)-(?:start|end)$/;
99+
return endStartPositionsRegExp.test(placement);
100+
}
101+
102+
private getOppositePlacement(placement: Placement): Placement {
103+
const oppositePlacements: { [key: string]: string } = {
104+
[Placement.TopStart]: Placement.TopEnd,
105+
[Placement.TopEnd]: Placement.TopStart,
106+
[Placement.BottomStart]: Placement.BottomEnd,
107+
[Placement.BottomEnd]: Placement.BottomStart,
108+
[Placement.LeftStart]: Placement.LeftEnd,
109+
[Placement.LeftEnd]: Placement.LeftStart,
110+
[Placement.RightStart]: Placement.RightEnd,
111+
[Placement.RightEnd]: Placement.RightStart,
112+
};
113+
114+
return oppositePlacements[placement] as Placement;
115+
}
116+
}

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

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ export enum VerticalAlignment {
2020
Bottom = 0
2121
}
2222

23+
/**
24+
* Defines the possible positions for the element relative to its target.
25+
*/
26+
export enum Placement {
27+
Top = 'top',
28+
TopStart = 'top-start',
29+
TopEnd = 'top-end',
30+
Bottom = 'bottom',
31+
BottomStart = 'bottom-start',
32+
BottomEnd = 'bottom-end',
33+
Right = 'right',
34+
RightStart = 'right-start',
35+
RightEnd = 'right-end',
36+
Left = 'left',
37+
LeftStart = 'left-start',
38+
LeftEnd = 'left-end'
39+
}
40+
2341
/**
2442
* Defines the possible values of the overlays' position strategy.
2543
*/
@@ -87,6 +105,10 @@ export interface PositionSettings {
87105
closeAnimation?: AnimationReferenceMetadata;
88106
/** The size up to which element may shrink when shown in elastic position strategy */
89107
minSize?: Size;
108+
/** Where to place the element relative to its target. Used to set the direction and starting point. */
109+
placement?: Placement;
110+
/** The offset of the element from the target in pixels when shown in connected position strategy. */
111+
offset?: number;
90112
}
91113

92114
export interface OverlaySettings {
@@ -261,4 +283,176 @@ export class Util {
261283
clonedObj.settings = cloneValue(clonedObj.settings);
262284
return clonedObj;
263285
}
286+
287+
/**
288+
* Gets the position settings that correspond to the given placement.
289+
*
290+
* @param placement Placement for which to get the corresponding position settings.
291+
*/
292+
public static getPositionSettingsByPlacement(placement: Placement): PositionSettings {
293+
return PositionsMap.get(placement);
294+
}
295+
296+
/**
297+
* Gets the placement that correspond to the given position settings.
298+
* Returns `undefined` if the position settings do not match any of the predefined placement values.
299+
*
300+
* @param settings Position settings for which to get the corresponding placement.
301+
*/
302+
public static getPlacementByPositionSettings(settings: PositionSettings): Placement {
303+
const { horizontalDirection, horizontalStartPoint, verticalDirection, verticalStartPoint } = settings;
304+
305+
const mapArray = Array.from(PositionsMap.entries());
306+
const placement = mapArray.find(
307+
([_, val]) =>
308+
val.horizontalDirection === horizontalDirection &&
309+
val.horizontalStartPoint === horizontalStartPoint &&
310+
val.verticalDirection === verticalDirection &&
311+
val.verticalStartPoint === verticalStartPoint
312+
);
313+
314+
return placement ? placement[0] : undefined;
315+
}
316+
317+
/**
318+
* Returns `false` if a direction or starting point is specified by
319+
* `horizontalDirection`, `horizontalStartPoint`, `verticalDirection`, or `verticalStartPoint`.
320+
*
321+
* @param settings Position settings for which to check if any directions or starting points are defined.
322+
*/
323+
public static canUsePlacement(settings: PositionSettings): boolean {
324+
return settings
325+
&& settings.placement
326+
&& settings.horizontalDirection == null
327+
&& settings.horizontalStartPoint == null
328+
&& settings.verticalDirection == null
329+
&& settings.verticalStartPoint == null;
330+
}
331+
332+
/**
333+
* Gets horizontal offset by connectedFit `horizontalOffset` or position settings `offset`.
334+
* ConnectedFit `horizontalOffset` has priority.
335+
*/
336+
public static getHorizontalOffset(connectedFit: ConnectedFit, settings: PositionSettings): number {
337+
if (connectedFit.horizontalOffset != null) {
338+
return connectedFit.horizontalOffset;
339+
}
340+
341+
if (
342+
settings.horizontalDirection === HorizontalAlignment.Left &&
343+
settings.horizontalStartPoint === HorizontalAlignment.Left
344+
) {
345+
return -settings.offset;
346+
} else if (
347+
settings.horizontalDirection === HorizontalAlignment.Right &&
348+
settings.horizontalStartPoint === HorizontalAlignment.Right
349+
) {
350+
return settings.offset;
351+
}
352+
353+
return 0;
354+
}
355+
356+
/**
357+
* Gets vertical offset by connectedFit `verticalOffset` or position settings `offset`.
358+
* ConnectedFit `verticalOffset` has priority.
359+
*/
360+
public static getVerticalOffset(connectedFit: ConnectedFit, settings: PositionSettings): number {
361+
if (connectedFit.verticalOffset != null) {
362+
return connectedFit.verticalOffset;
363+
}
364+
365+
if (
366+
settings.verticalDirection === VerticalAlignment.Top &&
367+
settings.verticalStartPoint === VerticalAlignment.Top
368+
) {
369+
return -settings.offset;
370+
} else if (
371+
settings.verticalDirection === VerticalAlignment.Bottom &&
372+
settings.verticalStartPoint === VerticalAlignment.Bottom
373+
) {
374+
return settings.offset;
375+
}
376+
377+
return 0;
378+
}
379+
264380
}
381+
382+
/**
383+
* Maps the predefined placement values to the corresponding directions and starting points.
384+
*/
385+
export const PositionsMap = new Map<Placement, PositionSettings>([
386+
[Placement.Top, {
387+
horizontalDirection: HorizontalAlignment.Center,
388+
horizontalStartPoint: HorizontalAlignment.Center,
389+
verticalDirection: VerticalAlignment.Top,
390+
verticalStartPoint: VerticalAlignment.Top,
391+
}],
392+
[Placement.TopStart, {
393+
horizontalDirection: HorizontalAlignment.Right,
394+
horizontalStartPoint: HorizontalAlignment.Left,
395+
verticalDirection: VerticalAlignment.Top,
396+
verticalStartPoint: VerticalAlignment.Top,
397+
}],
398+
[Placement.TopEnd, {
399+
horizontalDirection: HorizontalAlignment.Left,
400+
horizontalStartPoint: HorizontalAlignment.Right,
401+
verticalDirection: VerticalAlignment.Top,
402+
verticalStartPoint: VerticalAlignment.Top,
403+
}],
404+
[Placement.Bottom, {
405+
horizontalDirection: HorizontalAlignment.Center,
406+
horizontalStartPoint: HorizontalAlignment.Center,
407+
verticalDirection: VerticalAlignment.Bottom,
408+
verticalStartPoint: VerticalAlignment.Bottom,
409+
}],
410+
[Placement.BottomStart, {
411+
horizontalDirection: HorizontalAlignment.Right,
412+
horizontalStartPoint: HorizontalAlignment.Left,
413+
verticalDirection: VerticalAlignment.Bottom,
414+
verticalStartPoint: VerticalAlignment.Bottom,
415+
}],
416+
[Placement.BottomEnd, {
417+
horizontalDirection: HorizontalAlignment.Left,
418+
horizontalStartPoint: HorizontalAlignment.Right,
419+
verticalDirection: VerticalAlignment.Bottom,
420+
verticalStartPoint: VerticalAlignment.Bottom,
421+
}],
422+
[Placement.Right, {
423+
horizontalDirection: HorizontalAlignment.Right,
424+
horizontalStartPoint: HorizontalAlignment.Right,
425+
verticalDirection: VerticalAlignment.Middle,
426+
verticalStartPoint: VerticalAlignment.Middle,
427+
}],
428+
[Placement.RightStart, {
429+
horizontalDirection: HorizontalAlignment.Right,
430+
horizontalStartPoint: HorizontalAlignment.Right,
431+
verticalDirection: VerticalAlignment.Bottom,
432+
verticalStartPoint: VerticalAlignment.Top,
433+
}],
434+
[Placement.RightEnd, {
435+
horizontalDirection: HorizontalAlignment.Right,
436+
horizontalStartPoint: HorizontalAlignment.Right,
437+
verticalDirection: VerticalAlignment.Top,
438+
verticalStartPoint: VerticalAlignment.Bottom,
439+
}],
440+
[Placement.Left, {
441+
horizontalDirection: HorizontalAlignment.Left,
442+
horizontalStartPoint: HorizontalAlignment.Left,
443+
verticalDirection: VerticalAlignment.Middle,
444+
verticalStartPoint: VerticalAlignment.Middle,
445+
}],
446+
[Placement.LeftStart, {
447+
horizontalDirection: HorizontalAlignment.Left,
448+
horizontalStartPoint: HorizontalAlignment.Left,
449+
verticalDirection: VerticalAlignment.Bottom,
450+
verticalStartPoint: VerticalAlignment.Top,
451+
}],
452+
[Placement.LeftEnd, {
453+
horizontalDirection: HorizontalAlignment.Left,
454+
horizontalStartPoint: HorizontalAlignment.Left,
455+
verticalDirection: VerticalAlignment.Top,
456+
verticalStartPoint: VerticalAlignment.Bottom,
457+
}]
458+
]);

projects/igniteui-angular/src/lib/services/public_api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export * from './overlay/position';
1111
export * from './overlay/scroll';
1212
export {
1313
AbsolutePosition, HorizontalAlignment, OverlayAnimationEventArgs, OverlayCancelableEventArgs, OverlayClosingEventArgs,
14-
OverlayCreateSettings, OverlayEventArgs, OverlaySettings, Point, PositionSettings, RelativePosition, RelativePositionStrategy, VerticalAlignment
14+
OverlayCreateSettings, OverlayEventArgs, OverlaySettings, Placement, Point, PositionSettings, RelativePosition, RelativePositionStrategy, VerticalAlignment
1515
} from './overlay/utilities';
1616
export * from './transaction/base-transaction';
1717
export * from './transaction/hierarchical-transaction';

0 commit comments

Comments
 (0)