Index: third_party/WebKit/Source/devtools/front_end/ui/ModalOverlay.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ModalOverlay.js b/third_party/WebKit/Source/devtools/front_end/ui/ModalOverlay.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..04b384d806c580f1c5a077f9c3e80b2a027a9721 |
--- /dev/null |
+++ b/third_party/WebKit/Source/devtools/front_end/ui/ModalOverlay.js |
@@ -0,0 +1,229 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+UI.ModalOverlay = class { |
pfeldman
2017/01/31 21:30:18
Could you clarify when this is used and how? The m
dgozman
2017/01/31 23:39:49
Updated.
|
+ constructor() { |
+ this.element = createElementWithClass('div', 'modal-overlay'); |
+ this._visible = false; |
+ this._blockPointerEvents = true; |
+ this._dimmed = false; |
+ /** @type {?UI.Size} */ |
+ this._maxSize = null; |
+ /** @type {?number} */ |
+ this._positionX = null; |
+ /** @type {?number} */ |
+ this._positionY = null; |
+ /** @type {?AnchorBox} */ |
+ this._anchorBox = null; |
+ /** @type {?UI.GlassPane} */ |
+ this._glassPane = null; |
+ this._anchorBehavior = UI.ModalOverlay.AnchorBehavior.PreferTop; |
+ } |
+ |
+ /** |
+ * @param {boolean} blockPointerEvents |
+ */ |
+ setBlockPointerEvents(blockPointerEvents) { |
+ this._blockPointerEvents = blockPointerEvents; |
+ } |
+ |
+ /** |
+ * @param {?UI.Size} size |
+ */ |
+ setMaxSize(size) { |
+ this._maxSize = size; |
+ } |
+ |
+ /** |
+ * @param {boolean} dimmed |
+ */ |
+ setDimmed(dimmed) { |
+ this._dimmed = dimmed; |
+ } |
+ |
+ /** |
+ * @param {?number} x |
+ * @param {?number} y |
+ * Position is relative to root element. |
+ */ |
+ setPosition(x, y) { |
+ this._positionX = x; |
+ this._positionY = y; |
+ } |
+ |
+ /** |
+ * @param {?AnchorBox} anchorBox |
+ * Anchor box is relative to the document. |
+ * Showing under anchor if more space. |
+ */ |
+ setAnchorBox(anchorBox) { |
+ this._anchorBox = anchorBox; |
+ } |
+ |
+ /** |
+ * @param {!UI.ModalOverlay.AnchorBehavior} behavior |
+ */ |
+ setAnchorBehavior(behavior) { |
+ this._anchorBehavior = behavior; |
+ } |
+ |
+ /** |
+ * @return {!Document} |
+ */ |
+ document() { |
+ return /** @type {!Document} */ (UI.ModalOverlay._rootElement.ownerDocument); |
+ } |
+ |
+ show() { |
+ if (!this._visible) { |
caseq
2017/01/31 21:27:20
nit: if (this._visible) return;
dgozman
2017/01/31 23:39:49
Done.
|
+ this._visible = true; |
+ this._glassPane = new UI.GlassPane(this.document(), this._dimmed, this._blockPointerEvents); |
+ this._glassPane.element.appendChild(this.element); |
+ UI.ModalOverlay._overlays.add(this); |
+ } |
+ } |
+ |
+ hide() { |
+ if (!this._visible) |
+ return; |
+ this._visible = false; |
+ this._glassPane.element.removeChild(this.element); |
+ this._glassPane.dispose(); |
+ this._glassPane = null; |
+ UI.ModalOverlay._overlays.delete(this); |
+ } |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ visible() { |
+ return this._visible; |
+ } |
+ |
+ /** |
+ * @param {string} eventType |
+ * @param {function(!Event)} listener |
+ * @param {boolean=} capture |
+ */ |
+ addGlassPaneEventListener(eventType, listener, capture) { |
caseq
2017/01/31 21:27:20
should we make the interface simpler by converting
dgozman
2017/01/31 23:39:49
Simplified.
|
+ console.assert(this._visible); |
+ this._glassPane.element.addEventListener(eventType, listener, capture); |
+ } |
+ |
+ /** |
+ * @param {string} eventType |
+ * @param {function(!Event)} listener |
+ * @param {boolean=} capture |
+ */ |
+ removeGlassPaneEventListener(eventType, listener, capture) { |
+ console.assert(this._visible); |
+ this._glassPane.element.removeEventListener(eventType, listener, capture); |
+ } |
+ |
+ position() { |
+ if (!this._visible) |
+ return; |
+ |
+ var gutter = 5; |
caseq
2017/01/31 21:27:20
nit: gutterSize or gutterPixels
dgozman
2017/01/31 23:39:49
Done.
|
+ var container = UI.ModalOverlay._rootElement; |
+ var containerWidth = container.offsetWidth; |
+ var containerHeight = container.offsetHeight; |
+ |
+ var width = containerWidth - gutter * 2; |
+ var height = containerHeight - gutter * 2; |
+ var positionX = gutter; |
+ var positionY = gutter; |
+ |
+ if (this._maxSize) { |
+ width = Math.min(width, this._maxSize.width); |
+ height = Math.min(height, this._maxSize.height); |
+ } |
+ |
+ if (this._anchorBox) { |
+ var anchorBox = this._anchorBox.relativeToElement(container); |
+ var topHeight = anchorBox.y - gutter; |
+ var bottomHeight = containerHeight - anchorBox.y - anchorBox.height - gutter; |
+ var leftWidth = anchorBox.x - gutter; |
+ var rightWidth = containerWidth - anchorBox.x - anchorBox.width - gutter; |
caseq
2017/01/31 21:27:20
nit: Height/Width looks a bit artificial above. Pe
dgozman
2017/01/31 23:39:49
Reworked.
|
+ |
+ var behavior = this._anchorBehavior; |
+ if (behavior === UI.ModalOverlay.AnchorBehavior.PreferTop && topHeight < height && bottomHeight >= height) |
+ behavior = UI.ModalOverlay.AnchorBehavior.PreferBottom; |
+ if (behavior === UI.ModalOverlay.AnchorBehavior.PreferBottom && bottomHeight < height && topHeight >= height) |
+ behavior = UI.ModalOverlay.AnchorBehavior.PreferTop; |
+ if (behavior === UI.ModalOverlay.AnchorBehavior.PreferLeft && leftWidth < width && rightWidth >= width) |
+ behavior = UI.ModalOverlay.AnchorBehavior.PreferRight; |
+ if (behavior === UI.ModalOverlay.AnchorBehavior.PreferRight && rightWidth < width && leftWidth >= width) |
+ behavior = UI.ModalOverlay.AnchorBehavior.PreferLeft; |
+ |
+ if (behavior === UI.ModalOverlay.AnchorBehavior.PreferTop || |
+ behavior === UI.ModalOverlay.AnchorBehavior.PreferBottom) { |
+ positionX = Math.max(gutter, Math.min(anchorBox.x, containerWidth - width - gutter)); |
+ width = Math.min(width, containerWidth - positionX - gutter); |
+ if (behavior === UI.ModalOverlay.AnchorBehavior.PreferTop) { |
+ positionY = Math.max(gutter, anchorBox.y - height); |
+ height = Math.min(height, anchorBox.y - positionY); |
+ } else { |
+ positionY = anchorBox.y + anchorBox.height; |
+ height = Math.min(height, containerHeight - positionY - gutter); |
+ } |
+ } else { |
+ positionY = Math.max(gutter, Math.min(anchorBox.y, containerHeight - height - gutter)); |
+ height = Math.min(height, containerHeight - positionY - gutter); |
+ if (behavior === UI.ModalOverlay.AnchorBehavior.PreferLeft) { |
+ positionX = Math.max(gutter, anchorBox.x - width); |
+ width = Math.min(width, anchorBox.x - positionX); |
+ } else { |
+ positionX = anchorBox.x + anchorBox.width; |
+ width = Math.min(width, containerWidth - positionX - gutter); |
+ } |
+ } |
+ } else { |
+ positionX = this._positionX !== null ? this._positionX : (containerWidth - width) / 2; |
+ positionY = this._positionY !== null ? this._positionY : (containerHeight - height) / 2; |
+ width = Math.min(width, containerWidth - positionX - gutter); |
+ height = Math.min(height, containerHeight - positionY - gutter); |
+ } |
+ |
+ this.element.style.width = width + 'px'; |
+ this.element.style.height = height + 'px'; |
+ this.element.positionAt(positionX, positionY, container); |
+ } |
+ |
+ /** |
+ * @param {!Element} element |
+ */ |
+ static setRootElement(element) { |
+ UI.ModalOverlay._rootElement = element; |
+ UI.ModalOverlay.rootElementMoved(); |
+ } |
+ |
+ /** |
+ * @return {!Element} |
+ */ |
+ static rootElement() { |
+ return UI.ModalOverlay._rootElement; |
+ } |
+ |
+ static rootElementMoved() { |
+ for (var overlay of UI.ModalOverlay._overlays) |
+ overlay.position(); |
+ } |
+}; |
+ |
+/** |
+ * @enum {symbol} |
+ */ |
+UI.ModalOverlay.AnchorBehavior = { |
+ PreferTop: Symbol('PreferTop'), |
+ PreferBottom: Symbol('PreferBottom'), |
+ PreferLeft: Symbol('PreferLeft'), |
+ PreferRight: Symbol('PreferRight'), |
+}; |
+ |
+// TODO(dgozman): if we want to show modals in different documents, this should be a map. |
+/** @type {!Element} */ |
+UI.ModalOverlay._rootElement; |
+/** @type {!Set<!UI.ModalOverlay>} */ |
+UI.ModalOverlay._overlays = new Set(); |