Chromium Code Reviews| 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(); |