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

Commit 8481323

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 8481323

File tree

3 files changed

+268
-140
lines changed

3 files changed

+268
-140
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: 84 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -433,51 +433,107 @@ angular.module('ui.bootstrap.position', [])
433433
*/
434434
positionElements: function(hostElem, targetElem, placement, appendToBody) {
435435
hostElem = this.getRawNode(hostElem);
436-
targetElem = this.getRawNode(targetElem);
437436

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) {
438495
// need to read from prop to support tests.
439496
var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
440497
var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
441498

499+
targetElem = this.getRawNode(targetElem);
500+
442501
placement = this.parsePlacement(placement);
443502

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

447505
if (placement[2]) {
448-
var viewportOffset = this.viewportOffset(hostElem, appendToBody);
449-
450506
var targetElemStyle = $window.getComputedStyle(targetElem);
451507
var adjustedSize = {
452508
width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
453509
height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
454510
};
455511

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' :
512+
placement[0] = placement[0] === 'top' && adjustedSize.height > hostOffset.top && adjustedSize.height <= hostOffset.bottom ? 'bottom' :
513+
placement[0] === 'bottom' && adjustedSize.height > hostOffset.bottom && adjustedSize.height <= hostOffset.top ? 'top' :
514+
placement[0] === 'left' && adjustedSize.width > hostOffset.left && adjustedSize.width <= hostOffset.right ? 'right' :
515+
placement[0] === 'right' && adjustedSize.width > hostOffset.right && adjustedSize.width <= hostOffset.left ? 'left' :
460516
placement[0];
461517

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' :
518+
placement[1] = placement[1] === 'top' && adjustedSize.height - hostPos.height > hostOffset.bottom && adjustedSize.height - hostPos.height <= hostOffset.top ? 'bottom' :
519+
placement[1] === 'bottom' && adjustedSize.height - hostPos.height > hostOffset.top && adjustedSize.height - hostPos.height <= hostOffset.bottom ? 'top' :
520+
placement[1] === 'left' && adjustedSize.width - hostPos.width > hostOffset.right && adjustedSize.width - hostPos.width <= hostOffset.left ? 'right' :
521+
placement[1] === 'right' && adjustedSize.width - hostPos.width > hostOffset.left && adjustedSize.width - hostPos.width <= hostOffset.right ? 'left' :
466522
placement[1];
467523

468524
if (placement[1] === 'center') {
469525
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) {
526+
var xOverflow = hostPos.width / 2 - targetWidth / 2;
527+
if (hostOffset.left + xOverflow < 0 && adjustedSize.width - hostPos.width <= hostOffset.right) {
472528
placement[1] = 'left';
473-
} else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
529+
} else if (hostOffset.right + xOverflow < 0 && adjustedSize.width - hostPos.width <= hostOffset.left) {
474530
placement[1] = 'right';
475531
}
476532
} else {
477-
var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
478-
if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
533+
var yOverflow = hostPos.height / 2 - adjustedSize.height / 2;
534+
if (hostOffset.top + yOverflow < 0 && adjustedSize.height - hostPos.height <= hostOffset.bottom) {
479535
placement[1] = 'top';
480-
} else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
536+
} else if (hostOffset.bottom + yOverflow < 0 && adjustedSize.height - hostPos.height <= hostOffset.top) {
481537
placement[1] = 'bottom';
482538
}
483539
}
@@ -486,37 +542,37 @@ angular.module('ui.bootstrap.position', [])
486542

487543
switch (placement[0]) {
488544
case 'top':
489-
targetElemPos.top = hostElemPos.top - targetHeight;
545+
targetElemPos.top = hostPos.top - targetHeight;
490546
break;
491547
case 'bottom':
492-
targetElemPos.top = hostElemPos.top + hostElemPos.height;
548+
targetElemPos.top = hostPos.top + hostPos.height;
493549
break;
494550
case 'left':
495-
targetElemPos.left = hostElemPos.left - targetWidth;
551+
targetElemPos.left = hostPos.left - targetWidth;
496552
break;
497553
case 'right':
498-
targetElemPos.left = hostElemPos.left + hostElemPos.width;
554+
targetElemPos.left = hostPos.left + hostPos.width;
499555
break;
500556
}
501557

502558
switch (placement[1]) {
503559
case 'top':
504-
targetElemPos.top = hostElemPos.top;
560+
targetElemPos.top = hostPos.top;
505561
break;
506562
case 'bottom':
507-
targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
563+
targetElemPos.top = hostPos.top + hostPos.height - targetHeight;
508564
break;
509565
case 'left':
510-
targetElemPos.left = hostElemPos.left;
566+
targetElemPos.left = hostPos.left;
511567
break;
512568
case 'right':
513-
targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
569+
targetElemPos.left = hostPos.left + hostPos.width - targetWidth;
514570
break;
515571
case 'center':
516572
if (PLACEMENT_REGEX.vertical.test(placement[0])) {
517-
targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
573+
targetElemPos.left = hostPos.left + hostPos.width / 2 - targetWidth / 2;
518574
} else {
519-
targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
575+
targetElemPos.top = hostPos.top + hostPos.height / 2 - targetHeight / 2;
520576
}
521577
break;
522578
}

0 commit comments

Comments
 (0)