1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview This file provides utility functions for position popups.
9 cr.exportPath('cr.ui');
12 * Type def for rects as returned by getBoundingClientRect.
13 * @typedef {{left: number, top: number, width: number, height: number,
14 * right: number, bottom: number}}
19 * Enum for defining how to anchor a popup to an anchor element.
24 * The popup's right edge is aligned with the left edge of the anchor.
25 * The popup's top edge is aligned with the top edge of the anchor.
27 BEFORE: 1, // p: right, a: left, p: top, a: top
30 * The popop's left edge is aligned with the right edge of the anchor.
31 * The popup's top edge is aligned with the top edge of the anchor.
33 AFTER: 2, // p: left a: right, p: top, a: top
36 * The popop's bottom edge is aligned with the top edge of the anchor.
37 * The popup's left edge is aligned with the left edge of the anchor.
39 ABOVE: 3, // p: bottom, a: top, p: left, a: left
42 * The popop's top edge is aligned with the bottom edge of the anchor.
43 * The popup's left edge is aligned with the left edge of the anchor.
45 BELOW: 4 // p: top, a: bottom, p: left, a: left
48 cr.define('cr.ui', function() {
50 var AnchorType = cr.ui.AnchorType;
53 * Helper function for positionPopupAroundElement and positionPopupAroundRect.
54 * @param {!cr.ui.Rect} anchorRect The rect for the anchor.
55 * @param {!HTMLElement} popupElement The element used for the popup.
56 * @param {cr.ui.AnchorType} type The type of anchoring to do.
57 * @param {boolean=} opt_invertLeftRight Whether to invert the right/left
60 function positionPopupAroundRect(anchorRect, popupElement, type,
61 opt_invertLeftRight) {
62 var popupRect = popupElement.getBoundingClientRect();
64 var ownerDoc = popupElement.ownerDocument;
65 var cs = ownerDoc.defaultView.getComputedStyle(popupElement);
66 var docElement = ownerDoc.documentElement;
68 if (cs.position == 'fixed') {
69 // For 'fixed' positioned popups, the available rectangle should be based
70 // on the viewport rather than the document.
72 height: docElement.clientHeight,
73 width: docElement.clientWidth,
75 bottom: docElement.clientHeight,
77 right: docElement.clientWidth
80 availRect = popupElement.offsetParent.getBoundingClientRect();
83 if (cs.direction == 'rtl')
84 opt_invertLeftRight = !opt_invertLeftRight;
86 // Flip BEFORE, AFTER based on alignment.
87 if (opt_invertLeftRight) {
88 if (type == AnchorType.BEFORE)
89 type = AnchorType.AFTER;
90 else if (type == AnchorType.AFTER)
91 type = AnchorType.BEFORE;
94 // Flip type based on available size
96 case AnchorType.BELOW:
97 if (anchorRect.bottom + popupRect.height > availRect.height &&
98 popupRect.height <= anchorRect.top) {
99 type = AnchorType.ABOVE;
102 case AnchorType.ABOVE:
103 if (popupRect.height > anchorRect.top &&
104 anchorRect.bottom + popupRect.height <= availRect.height) {
105 type = AnchorType.BELOW;
108 case AnchorType.AFTER:
109 if (anchorRect.right + popupRect.width > availRect.width &&
110 popupRect.width <= anchorRect.left) {
111 type = AnchorType.BEFORE;
114 case AnchorType.BEFORE:
115 if (popupRect.width > anchorRect.left &&
116 anchorRect.right + popupRect.width <= availRect.width) {
117 type = AnchorType.AFTER;
123 var style = popupElement.style;
124 // Reset all directions.
125 style.left = style.right = style.top = style.bottom = 'auto';
129 case AnchorType.BELOW:
130 if (anchorRect.bottom + popupRect.height <= availRect.height)
131 style.top = anchorRect.bottom + 'px';
135 case AnchorType.ABOVE:
136 if (availRect.height - anchorRect.top >= 0)
137 style.bottom = availRect.height - anchorRect.top + 'px';
141 case AnchorType.AFTER:
142 if (anchorRect.right + popupRect.width <= availRect.width)
143 style.left = anchorRect.right + 'px';
147 case AnchorType.BEFORE:
148 if (availRect.width - anchorRect.left >= 0)
149 style.right = availRect.width - anchorRect.left + 'px';
155 // Secondary direction
157 case AnchorType.BELOW:
158 case AnchorType.ABOVE:
159 if (opt_invertLeftRight) {
161 if (anchorRect.right - popupRect.width >= 0) {
162 style.right = availRect.width - anchorRect.right + 'px';
165 } else if (anchorRect.left + popupRect.width <= availRect.width) {
166 style.left = anchorRect.left + 'px';
168 // not enough room on either side
174 if (anchorRect.left + popupRect.width <= availRect.width) {
175 style.left = anchorRect.left + 'px';
178 } else if (anchorRect.right - popupRect.width >= 0) {
179 style.right = availRect.width - anchorRect.right + 'px';
181 // not enough room on either side
188 case AnchorType.AFTER:
189 case AnchorType.BEFORE:
191 if (anchorRect.top + popupRect.height <= availRect.height) {
192 style.top = anchorRect.top + 'px';
194 // align bottom edges
195 } else if (anchorRect.bottom - popupRect.height >= 0) {
196 style.bottom = availRect.height - anchorRect.bottom + 'px';
198 // not enough room on either side
207 * Positions a popup element relative to an anchor element. The popup element
208 * should have position set to absolute and it should be a child of the body
210 * @param {!HTMLElement} anchorElement The element that the popup is anchored
212 * @param {!HTMLElement} popupElement The popup element we are positioning.
213 * @param {cr.ui.AnchorType} type The type of anchoring we want.
214 * @param {boolean=} opt_invertLeftRight Whether to invert the right/left
217 function positionPopupAroundElement(anchorElement, popupElement, type,
218 opt_invertLeftRight) {
219 var anchorRect = anchorElement.getBoundingClientRect();
220 positionPopupAroundRect(anchorRect, popupElement, type,
221 !!opt_invertLeftRight);
225 * Positions a popup around a point.
226 * @param {number} x The client x position.
227 * @param {number} y The client y position.
228 * @param {!HTMLElement} popupElement The popup element we are positioning.
230 function positionPopupAtPoint(x, y, popupElement) {
239 positionPopupAroundRect(rect, popupElement, AnchorType.BELOW);
244 positionPopupAroundElement: positionPopupAroundElement,
245 positionPopupAtPoint: positionPopupAtPoint