Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 94b766b

Browse files
Umer FarooqUmer Farooq
authored andcommitted
feat(position): add 'positionElementAt' method
Add a method to position an element at client x/y coordinates. Using code from the positionElements method, which has been refactored into a positionElement method with both positionElementAt and positionElements use.
1 parent a4d7076 commit 94b766b

File tree

3 files changed

+270
-145
lines changed

3 files changed

+270
-145
lines changed

src/position/docs/readme.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,40 @@ Gets gets coordinates for an element to be positioned relative to another elemen
309309

310310
An object with the following properties:
311311

312+
* `top`
313+
_(Type: `number`)_ -
314+
The targetElement top value.
315+
316+
* `left`
317+
_(Type: `number`)_ -
318+
The targetElement left value.
319+
320+
* `right`
321+
_(Type: `number`)_ -
322+
The resolved placement with 'auto' removed.
323+
324+
#### positionElementAt(clientCoordinates, targetElement, placement)
325+
326+
Gets gets coordinates for an element to be positioned at relative to the horizontal and vertical client coordinates.
327+
328+
##### parameters
329+
330+
* `clientCoordinates`
331+
_(Type: `Object`)_ -
332+
The clientX and clientY coordinates to position against.
333+
334+
* `targetElement`
335+
_(Type: `element`)_ -
336+
The element to position.
337+
338+
* `placement`
339+
_(Type: `string`, Default: `top`, optional)_ -
340+
The placement for the target element. See the parsePlacement() function for available options. If 'auto' placement is used, the viewportOffset() function is used to decide where the targetElement will fit.
341+
342+
##### returns
343+
344+
An object with the following properties:
345+
312346
* `top`
313347
_(Type: `number`)_ -
314348
The targetElement top value.

src/position/position.js

Lines changed: 86 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -433,51 +433,106 @@ angular.module('ui.bootstrap.position', [])
433433
*/
434434
positionElements: function(hostElem, targetElem, placement, appendToBody) {
435435
hostElem = this.getRawNode(hostElem);
436-
targetElem = this.getRawNode(targetElem);
437436

438-
// need to read from prop to support tests.
439-
var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
440-
var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
437+
var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
438+
var viewportOffset = this.viewportOffset(hostElem, appendToBody);
439+
440+
return this.positionElement(viewportOffset, hostElemPos, targetElem, placement);
441+
},
442+
443+
/**
444+
* Provides coordinates for an element to be positioned at relative to
445+
* the horizontal and vertical client coordinates. Passing 'auto' as part of the placement parameter
446+
* will enable smart placement - where the element fits. i.e:
447+
* 'auto left-top' will check to see if there is enough space to the left
448+
* of the client coordinates to fit the targetElem, if not place right (same for secondary
449+
* top placement). Available space is calculated using the client width and height.
450+
*
451+
* @param {element} clientCoordinates - The coordinates to position at.
452+
* @param {element} targetElem - The element to position.
453+
* @param {string=} [placement=top] - The placement for the targetElem,
454+
* default is 'top'. 'center' is assumed as secondary placement for
455+
* 'top', 'left', 'right', and 'bottom' placements. Available placements are:
456+
* <ul>
457+
* <li>top</li>
458+
* <li>top-right</li>
459+
* <li>top-left</li>
460+
* <li>bottom</li>
461+
* <li>bottom-left</li>
462+
* <li>bottom-right</li>
463+
* <li>left</li>
464+
* <li>left-top</li>
465+
* <li>left-bottom</li>
466+
* <li>right</li>
467+
* <li>right-top</li>
468+
* <li>right-bottom</li>
469+
* </ul>
470+
*
471+
* @returns {object} An object with the following properties:
472+
* <ul>
473+
* <li>**top**: Value for targetElem top.</li>
474+
* <li>**left**: Value for targetElem left.</li>
475+
* <li>**placement**: The resolved placement.</li>
476+
* </ul>
477+
*/
478+
positionElementAt: function(clientCoordinates, targetElem, placement) {
479+
var viewportOffset = {
480+
top: clientCoordinates.clientY,
481+
left: clientCoordinates.clientX,
482+
right: $document[0].documentElement.clientWidth - clientCoordinates.clientX,
483+
bottom: $document[0].documentElement.clientHeight - clientCoordinates.clientY
484+
};
485+
var hostPosition = {
486+
top: clientCoordinates.clientY,
487+
left: clientCoordinates.clientX,
488+
height: 0,
489+
width: 0
490+
};
491+
return this.positionElement(viewportOffset, hostPosition, targetElem, placement);
492+
},
493+
494+
positionElement: function(hostOffset, hostPos, targetElem, placement) {
495+
var targetWidth = targetElem.innerWidth();
496+
var targetHeight = targetElem.innerHeight();
497+
498+
targetElem = this.getRawNode(targetElem);
441499

442500
placement = this.parsePlacement(placement);
443501

444-
var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
445502
var targetElemPos = {top: 0, left: 0, placement: ''};
446503

447504
if (placement[2]) {
448-
var viewportOffset = this.viewportOffset(hostElem, appendToBody);
449-
450505
var targetElemStyle = $window.getComputedStyle(targetElem);
451506
var adjustedSize = {
452507
width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
453508
height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
454509
};
455510

456-
placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
457-
placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
458-
placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
459-
placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
511+
placement[0] = placement[0] === 'top' && adjustedSize.height > hostOffset.top && adjustedSize.height <= hostOffset.bottom ? 'bottom' :
512+
placement[0] === 'bottom' && adjustedSize.height > hostOffset.bottom && adjustedSize.height <= hostOffset.top ? 'top' :
513+
placement[0] === 'left' && adjustedSize.width > hostOffset.left && adjustedSize.width <= hostOffset.right ? 'right' :
514+
placement[0] === 'right' && adjustedSize.width > hostOffset.right && adjustedSize.width <= hostOffset.left ? 'left' :
460515
placement[0];
461516

462-
placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
463-
placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
464-
placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
465-
placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
517+
placement[1] = placement[1] === 'top' && adjustedSize.height - hostPos.height > hostOffset.bottom && adjustedSize.height - hostPos.height <= hostOffset.top ? 'bottom' :
518+
placement[1] === 'bottom' && adjustedSize.height - hostPos.height > hostOffset.top && adjustedSize.height - hostPos.height <= hostOffset.bottom ? 'top' :
519+
placement[1] === 'left' && adjustedSize.width - hostPos.width > hostOffset.right && adjustedSize.width - hostPos.width <= hostOffset.left ? 'right' :
520+
placement[1] === 'right' && adjustedSize.width - hostPos.width > hostOffset.left && adjustedSize.width - hostPos.width <= hostOffset.right ? 'left' :
466521
placement[1];
467522

468523
if (placement[1] === 'center') {
469524
if (PLACEMENT_REGEX.vertical.test(placement[0])) {
470-
var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
471-
if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
525+
var xOverflow = hostPos.width / 2 - targetWidth / 2;
526+
if (hostOffset.left + xOverflow < 0 && adjustedSize.width - hostPos.width <= hostOffset.right) {
472527
placement[1] = 'left';
473-
} else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
528+
} else if (hostOffset.right + xOverflow < 0 && adjustedSize.width - hostPos.width <= hostOffset.left) {
474529
placement[1] = 'right';
475530
}
476531
} else {
477-
var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
478-
if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
532+
var yOverflow = hostPos.height / 2 - adjustedSize.height / 2;
533+
if (hostOffset.top + yOverflow < 0 && adjustedSize.height - hostPos.height <= hostOffset.bottom) {
479534
placement[1] = 'top';
480-
} else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
535+
} else if (hostOffset.bottom + yOverflow < 0 && adjustedSize.height - hostPos.height <= hostOffset.top) {
481536
placement[1] = 'bottom';
482537
}
483538
}
@@ -486,37 +541,37 @@ angular.module('ui.bootstrap.position', [])
486541

487542
switch (placement[0]) {
488543
case 'top':
489-
targetElemPos.top = hostElemPos.top - targetHeight;
544+
targetElemPos.top = hostPos.top - targetHeight;
490545
break;
491546
case 'bottom':
492-
targetElemPos.top = hostElemPos.top + hostElemPos.height;
547+
targetElemPos.top = hostPos.top + hostPos.height;
493548
break;
494549
case 'left':
495-
targetElemPos.left = hostElemPos.left - targetWidth;
550+
targetElemPos.left = hostPos.left - targetWidth;
496551
break;
497552
case 'right':
498-
targetElemPos.left = hostElemPos.left + hostElemPos.width;
553+
targetElemPos.left = hostPos.left + hostPos.width;
499554
break;
500555
}
501556

502557
switch (placement[1]) {
503558
case 'top':
504-
targetElemPos.top = hostElemPos.top;
559+
targetElemPos.top = hostPos.top;
505560
break;
506561
case 'bottom':
507-
targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
562+
targetElemPos.top = hostPos.top + hostPos.height - targetHeight;
508563
break;
509564
case 'left':
510-
targetElemPos.left = hostElemPos.left;
565+
targetElemPos.left = hostPos.left;
511566
break;
512567
case 'right':
513-
targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
568+
targetElemPos.left = hostPos.left + hostPos.width - targetWidth;
514569
break;
515570
case 'center':
516571
if (PLACEMENT_REGEX.vertical.test(placement[0])) {
517-
targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
572+
targetElemPos.left = hostPos.left + hostPos.width / 2 - targetWidth / 2;
518573
} else {
519-
targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
574+
targetElemPos.top = hostPos.top + hostPos.height / 2 - targetHeight / 2;
520575
}
521576
break;
522577
}

0 commit comments

Comments
 (0)