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

Commit 3704db9

Browse files
feat(tooltip): support more positioning options
Closes #1676
1 parent 1ba07c1 commit 3704db9

File tree

3 files changed

+173
-41
lines changed

3 files changed

+173
-41
lines changed

src/position/position.js

+72
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,78 @@ angular.module('ui.bootstrap.position', [])
7575
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
7676
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
7777
};
78+
},
79+
80+
/**
81+
* Provides coordinates for the targetEl in relation to hostEl
82+
*/
83+
positionElements: function (hostEl, targetEl, positionStr, appendToBody) {
84+
85+
var positionStrParts = positionStr.split('-');
86+
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
87+
88+
var hostElPos,
89+
targetElWidth,
90+
targetElHeight,
91+
targetElPos;
92+
93+
hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
94+
95+
targetElWidth = targetEl.prop('offsetWidth');
96+
targetElHeight = targetEl.prop('offsetHeight');
97+
98+
var shiftWidth = {
99+
center: function () {
100+
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
101+
},
102+
left: function () {
103+
return hostElPos.left;
104+
},
105+
right: function () {
106+
return hostElPos.left + hostElPos.width;
107+
}
108+
};
109+
110+
var shiftHeight = {
111+
center: function () {
112+
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
113+
},
114+
top: function () {
115+
return hostElPos.top;
116+
},
117+
bottom: function () {
118+
return hostElPos.top + hostElPos.height;
119+
}
120+
};
121+
122+
switch (pos0) {
123+
case 'right':
124+
targetElPos = {
125+
top: shiftHeight[pos1](),
126+
left: shiftWidth[pos0]()
127+
};
128+
break;
129+
case 'left':
130+
targetElPos = {
131+
top: shiftHeight[pos1](),
132+
left: hostElPos.left - targetElWidth
133+
};
134+
break;
135+
case 'bottom':
136+
targetElPos = {
137+
top: shiftHeight[pos0](),
138+
left: shiftWidth[pos1]()
139+
};
140+
break;
141+
default:
142+
targetElPos = {
143+
top: hostElPos.top - targetElHeight,
144+
left: shiftWidth[pos1]()
145+
};
146+
break;
147+
}
148+
149+
return targetElPos;
78150
}
79151
};
80152
}]);

src/position/test/position.spec.js

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
describe('position elements', function () {
2+
3+
var TargetElMock = function(width, height) {
4+
this.width = width;
5+
this.height = height;
6+
7+
this.prop = function(propName) {
8+
return propName === 'offsetWidth' ? width : height;
9+
};
10+
};
11+
12+
var $position;
13+
14+
beforeEach(module('ui.bootstrap.position'));
15+
beforeEach(inject(function (_$position_) {
16+
$position = _$position_;
17+
}));
18+
beforeEach(function () {
19+
this.addMatchers({
20+
toBePositionedAt: function(top, left) {
21+
this.message = function() {
22+
return 'Expected "(' + this.actual.top + ', ' + this.actual.left + ')" to be positioned at (' + top + ', ' + left + ')';
23+
};
24+
25+
return this.actual.top == top && this.actual.left == left;
26+
}
27+
});
28+
});
29+
30+
31+
describe('append-to-body: false', function () {
32+
33+
beforeEach(function () {
34+
//mock position info normally queried from the DOM
35+
$position.position = function() {
36+
return {
37+
width: 20,
38+
height: 20,
39+
top: 100,
40+
left: 100
41+
};
42+
};
43+
});
44+
45+
it('should position element on top-center by default', function () {
46+
expect($position.positionElements({}, new TargetElMock(10, 10), 'other')).toBePositionedAt(90, 105);
47+
expect($position.positionElements({}, new TargetElMock(10, 10), 'top')).toBePositionedAt(90, 105);
48+
expect($position.positionElements({}, new TargetElMock(10, 10), 'top-center')).toBePositionedAt(90, 105);
49+
});
50+
51+
it('should position on top-left', function () {
52+
expect($position.positionElements({}, new TargetElMock(10, 10), 'top-left')).toBePositionedAt(90, 100);
53+
});
54+
55+
it('should position on top-right', function () {
56+
expect($position.positionElements({}, new TargetElMock(10, 10), 'top-right')).toBePositionedAt(90, 120);
57+
});
58+
59+
it('should position elements on bottom-center when "bottom" specified', function () {
60+
expect($position.positionElements({}, new TargetElMock(10, 10), 'bottom')).toBePositionedAt(120, 105);
61+
expect($position.positionElements({}, new TargetElMock(10, 10), 'bottom-center')).toBePositionedAt(120, 105);
62+
});
63+
64+
it('should position elements on bottom-left', function () {
65+
expect($position.positionElements({}, new TargetElMock(10, 10), 'bottom-left')).toBePositionedAt(120, 100);
66+
});
67+
68+
it('should position elements on bottom-right', function () {
69+
expect($position.positionElements({}, new TargetElMock(10, 10), 'bottom-right')).toBePositionedAt(120, 120);
70+
});
71+
72+
it('should position elements on left-center when "left" specified', function () {
73+
expect($position.positionElements({}, new TargetElMock(10, 10), 'left')).toBePositionedAt(105, 90);
74+
expect($position.positionElements({}, new TargetElMock(10, 10), 'left-center')).toBePositionedAt(105, 90);
75+
});
76+
77+
it('should position elements on left-top when "left-top" specified', function () {
78+
expect($position.positionElements({}, new TargetElMock(10, 10), 'left-top')).toBePositionedAt(100, 90);
79+
});
80+
81+
it('should position elements on left-bottom when "left-bottom" specified', function () {
82+
expect($position.positionElements({}, new TargetElMock(10, 10), 'left-bottom')).toBePositionedAt(120, 90);
83+
});
84+
85+
it('should position elements on right-center when "right" specified', function () {
86+
expect($position.positionElements({}, new TargetElMock(10, 10), 'right')).toBePositionedAt(105, 120);
87+
expect($position.positionElements({}, new TargetElMock(10, 10), 'right-center')).toBePositionedAt(105, 120);
88+
});
89+
90+
it('should position elements on right-top when "right-top" specified', function () {
91+
expect($position.positionElements({}, new TargetElMock(10, 10), 'right-top')).toBePositionedAt(100, 120);
92+
});
93+
94+
it('should position elements on right-top when "right-top" specified', function () {
95+
expect($position.positionElements({}, new TargetElMock(10, 10), 'right-bottom')).toBePositionedAt(120, 120);
96+
});
97+
});
98+
99+
});

src/tooltip/tooltip.js

+2-41
Original file line numberDiff line numberDiff line change
@@ -119,53 +119,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
119119
var triggers = getTriggers( undefined );
120120
var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
121121

122-
var positionTooltip = function (){
123-
var position,
124-
ttWidth,
125-
ttHeight,
126-
ttPosition;
127-
// Get the position of the directive element.
128-
position = appendToBody ? $position.offset( element ) : $position.position( element );
129-
130-
// Get the height and width of the tooltip so we can center it.
131-
ttWidth = tooltip.prop( 'offsetWidth' );
132-
ttHeight = tooltip.prop( 'offsetHeight' );
133-
134-
// Calculate the tooltip's top and left coordinates to center it with
135-
// this directive.
136-
switch ( scope.tt_placement ) {
137-
case 'right':
138-
ttPosition = {
139-
top: position.top + position.height / 2 - ttHeight / 2,
140-
left: position.left + position.width
141-
};
142-
break;
143-
case 'bottom':
144-
ttPosition = {
145-
top: position.top + position.height,
146-
left: position.left + position.width / 2 - ttWidth / 2
147-
};
148-
break;
149-
case 'left':
150-
ttPosition = {
151-
top: position.top + position.height / 2 - ttHeight / 2,
152-
left: position.left - ttWidth
153-
};
154-
break;
155-
default:
156-
ttPosition = {
157-
top: position.top - ttHeight,
158-
left: position.left + position.width / 2 - ttWidth / 2
159-
};
160-
break;
161-
}
122+
var positionTooltip = function () {
162123

124+
var ttPosition = $position.positionElements(element, tooltip, scope.tt_placement, appendToBody);
163125
ttPosition.top += 'px';
164126
ttPosition.left += 'px';
165127

166128
// Now set the calculated positioning.
167129
tooltip.css( ttPosition );
168-
169130
};
170131

171132
// By default, the tooltip is not open.

0 commit comments

Comments
 (0)