mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-11 06:04:55 +08:00
Move and rename files. NOAUTOREVERT=true NOPRESUBMIT=true NOTREECHECKS=true Bug: 768828 TBR=darin@chromium.org NOTRY=true Change-Id: I66d3b155808bc5bdbf237b80208e1e552bcf7f28 Reviewed-on: https://chromium-review.googlesource.com/1001153 Reviewed-by: Blink Reformat <blink-reformat@chromium.org> Commit-Queue: Blink Reformat <blink-reformat@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#549061} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 0aee4434a4dba42a42abaea9bfbc0cd196a63bc1
403 lines
14 KiB
JavaScript
403 lines
14 KiB
JavaScript
// 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.GlassPane = class {
|
|
constructor() {
|
|
this._widget = new UI.Widget(true);
|
|
this._widget.markAsRoot();
|
|
this.element = this._widget.element;
|
|
this.contentElement = this._widget.contentElement;
|
|
this._arrowElement = UI.Icon.create('', 'arrow hidden');
|
|
this.element.shadowRoot.appendChild(this._arrowElement);
|
|
|
|
this.registerRequiredCSS('ui/glassPane.css');
|
|
this.setPointerEventsBehavior(UI.GlassPane.PointerEventsBehavior.PierceGlassPane);
|
|
|
|
this._onMouseDownBound = this._onMouseDown.bind(this);
|
|
/** @type {?function(!Event)} */
|
|
this._onClickOutsideCallback = null;
|
|
/** @type {?UI.Size} */
|
|
this._maxSize = null;
|
|
/** @type {?number} */
|
|
this._positionX = null;
|
|
/** @type {?number} */
|
|
this._positionY = null;
|
|
/** @type {?AnchorBox} */
|
|
this._anchorBox = null;
|
|
this._anchorBehavior = UI.GlassPane.AnchorBehavior.PreferTop;
|
|
this._sizeBehavior = UI.GlassPane.SizeBehavior.SetExactSize;
|
|
this._marginBehavior = UI.GlassPane.MarginBehavior.DefaultMargin;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isShowing() {
|
|
return this._widget.isShowing();
|
|
}
|
|
|
|
/**
|
|
* @param {string} cssFile
|
|
*/
|
|
registerRequiredCSS(cssFile) {
|
|
this._widget.registerRequiredCSS(cssFile);
|
|
}
|
|
|
|
/**
|
|
* @param {?Element} element
|
|
*/
|
|
setDefaultFocusedElement(element) {
|
|
this._widget.setDefaultFocusedElement(element);
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} dimmed
|
|
*/
|
|
setDimmed(dimmed) {
|
|
this.element.classList.toggle('dimmed-pane', dimmed);
|
|
}
|
|
|
|
/**
|
|
* @param {!UI.GlassPane.PointerEventsBehavior} pointerEventsBehavior
|
|
*/
|
|
setPointerEventsBehavior(pointerEventsBehavior) {
|
|
this.element.classList.toggle(
|
|
'no-pointer-events', pointerEventsBehavior !== UI.GlassPane.PointerEventsBehavior.BlockedByGlassPane);
|
|
this.contentElement.classList.toggle(
|
|
'no-pointer-events', pointerEventsBehavior === UI.GlassPane.PointerEventsBehavior.PierceContents);
|
|
}
|
|
|
|
/**
|
|
* @param {?function(!Event)} callback
|
|
*/
|
|
setOutsideClickCallback(callback) {
|
|
this._onClickOutsideCallback = callback;
|
|
}
|
|
|
|
/**
|
|
* @param {?UI.Size} size
|
|
*/
|
|
setMaxContentSize(size) {
|
|
this._maxSize = size;
|
|
this._positionContent();
|
|
}
|
|
|
|
/**
|
|
* @param {!UI.GlassPane.SizeBehavior} sizeBehavior
|
|
*/
|
|
setSizeBehavior(sizeBehavior) {
|
|
this._sizeBehavior = sizeBehavior;
|
|
this._positionContent();
|
|
}
|
|
|
|
/**
|
|
* @param {?number} x
|
|
* @param {?number} y
|
|
* Position is relative to root element.
|
|
*/
|
|
setContentPosition(x, y) {
|
|
this._positionX = x;
|
|
this._positionY = y;
|
|
this._positionContent();
|
|
}
|
|
|
|
/**
|
|
* @param {?AnchorBox} anchorBox
|
|
* Anchor box is relative to the document.
|
|
*/
|
|
setContentAnchorBox(anchorBox) {
|
|
this._anchorBox = anchorBox;
|
|
this._positionContent();
|
|
}
|
|
|
|
/**
|
|
* @param {!UI.GlassPane.AnchorBehavior} behavior
|
|
*/
|
|
setAnchorBehavior(behavior) {
|
|
this._anchorBehavior = behavior;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} behavior
|
|
*/
|
|
setMarginBehavior(behavior) {
|
|
this._marginBehavior = behavior;
|
|
this._arrowElement.classList.toggle('hidden', behavior !== UI.GlassPane.MarginBehavior.Arrow);
|
|
}
|
|
|
|
/**
|
|
* @param {!Document} document
|
|
*/
|
|
show(document) {
|
|
if (this.isShowing())
|
|
return;
|
|
// Deliberately starts with 3000 to hide other z-indexed elements below.
|
|
this.element.style.zIndex = 3000 + 1000 * UI.GlassPane._panes.size;
|
|
document.body.addEventListener('mousedown', this._onMouseDownBound, true);
|
|
this._widget.show(document.body);
|
|
UI.GlassPane._panes.add(this);
|
|
this._positionContent();
|
|
}
|
|
|
|
hide() {
|
|
if (!this.isShowing())
|
|
return;
|
|
UI.GlassPane._panes.delete(this);
|
|
this.element.ownerDocument.body.removeEventListener('mousedown', this._onMouseDownBound, true);
|
|
this._widget.detach();
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onMouseDown(event) {
|
|
if (!this._onClickOutsideCallback)
|
|
return;
|
|
const node = event.deepElementFromPoint();
|
|
if (!node || this.contentElement.isSelfOrAncestor(node))
|
|
return;
|
|
this._onClickOutsideCallback.call(null, event);
|
|
}
|
|
|
|
_positionContent() {
|
|
if (!this.isShowing())
|
|
return;
|
|
|
|
const showArrow = this._marginBehavior === UI.GlassPane.MarginBehavior.Arrow;
|
|
const gutterSize = showArrow ? 8 : (this._marginBehavior === UI.GlassPane.MarginBehavior.NoMargin ? 0 : 3);
|
|
const scrollbarSize = UI.measuredScrollbarWidth(this.element.ownerDocument);
|
|
const arrowSize = 10;
|
|
|
|
const container = UI.GlassPane._containers.get(/** @type {!Document} */ (this.element.ownerDocument));
|
|
if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) {
|
|
this.contentElement.positionAt(0, 0);
|
|
this.contentElement.style.width = '';
|
|
this.contentElement.style.maxWidth = '';
|
|
this.contentElement.style.height = '';
|
|
this.contentElement.style.maxHeight = '';
|
|
}
|
|
|
|
const containerWidth = container.offsetWidth;
|
|
const containerHeight = container.offsetHeight;
|
|
|
|
let width = containerWidth - gutterSize * 2;
|
|
let height = containerHeight - gutterSize * 2;
|
|
let positionX = gutterSize;
|
|
let positionY = gutterSize;
|
|
|
|
if (this._maxSize) {
|
|
width = Math.min(width, this._maxSize.width);
|
|
height = Math.min(height, this._maxSize.height);
|
|
}
|
|
|
|
if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) {
|
|
const measuredWidth = this.contentElement.offsetWidth;
|
|
const measuredHeight = this.contentElement.offsetHeight;
|
|
const widthOverflow = height < measuredHeight ? scrollbarSize : 0;
|
|
const heightOverflow = width < measuredWidth ? scrollbarSize : 0;
|
|
width = Math.min(width, measuredWidth + widthOverflow);
|
|
height = Math.min(height, measuredHeight + heightOverflow);
|
|
}
|
|
|
|
if (this._anchorBox) {
|
|
const anchorBox = this._anchorBox.relativeToElement(container);
|
|
let behavior = this._anchorBehavior;
|
|
this._arrowElement.classList.remove('arrow-none', 'arrow-top', 'arrow-bottom', 'arrow-left', 'arrow-right');
|
|
|
|
if (behavior === UI.GlassPane.AnchorBehavior.PreferTop || behavior === UI.GlassPane.AnchorBehavior.PreferBottom) {
|
|
const top = anchorBox.y - 2 * gutterSize;
|
|
const bottom = containerHeight - anchorBox.y - anchorBox.height - 2 * gutterSize;
|
|
if (behavior === UI.GlassPane.AnchorBehavior.PreferTop && top < height && bottom > top)
|
|
behavior = UI.GlassPane.AnchorBehavior.PreferBottom;
|
|
if (behavior === UI.GlassPane.AnchorBehavior.PreferBottom && bottom < height && top > bottom)
|
|
behavior = UI.GlassPane.AnchorBehavior.PreferTop;
|
|
|
|
let arrowY;
|
|
let enoughHeight = true;
|
|
if (behavior === UI.GlassPane.AnchorBehavior.PreferTop) {
|
|
positionY = Math.max(gutterSize, anchorBox.y - height - gutterSize);
|
|
const spaceTop = anchorBox.y - positionY - gutterSize;
|
|
if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) {
|
|
if (height > spaceTop) {
|
|
this._arrowElement.classList.add('arrow-none');
|
|
enoughHeight = false;
|
|
}
|
|
} else {
|
|
height = Math.min(height, spaceTop);
|
|
}
|
|
this._arrowElement.setIconType('mediumicon-arrow-bottom');
|
|
this._arrowElement.classList.add('arrow-bottom');
|
|
arrowY = anchorBox.y - gutterSize;
|
|
} else {
|
|
positionY = anchorBox.y + anchorBox.height + gutterSize;
|
|
const spaceBottom = containerHeight - positionY - gutterSize;
|
|
if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) {
|
|
if (height > spaceBottom) {
|
|
this._arrowElement.classList.add('arrow-none');
|
|
positionY = containerHeight - gutterSize - height;
|
|
enoughHeight = false;
|
|
}
|
|
} else {
|
|
height = Math.min(height, spaceBottom);
|
|
}
|
|
this._arrowElement.setIconType('mediumicon-arrow-top');
|
|
this._arrowElement.classList.add('arrow-top');
|
|
arrowY = anchorBox.y + anchorBox.height + gutterSize;
|
|
}
|
|
|
|
positionX = Math.max(gutterSize, Math.min(anchorBox.x, containerWidth - width - gutterSize));
|
|
if (!enoughHeight)
|
|
positionX = Math.min(positionX + arrowSize, containerWidth - width - gutterSize);
|
|
else if (showArrow && positionX - arrowSize >= gutterSize)
|
|
positionX -= arrowSize;
|
|
width = Math.min(width, containerWidth - positionX - gutterSize);
|
|
if (2 * arrowSize >= width) {
|
|
this._arrowElement.classList.add('arrow-none');
|
|
} else {
|
|
let arrowX = anchorBox.x + Math.min(50, Math.floor(anchorBox.width / 2));
|
|
arrowX = Number.constrain(arrowX, positionX + arrowSize, positionX + width - arrowSize);
|
|
this._arrowElement.positionAt(arrowX, arrowY, container);
|
|
}
|
|
} else {
|
|
const left = anchorBox.x - 2 * gutterSize;
|
|
const right = containerWidth - anchorBox.x - anchorBox.width - 2 * gutterSize;
|
|
if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft && left < width && right > left)
|
|
behavior = UI.GlassPane.AnchorBehavior.PreferRight;
|
|
if (behavior === UI.GlassPane.AnchorBehavior.PreferRight && right < width && left > right)
|
|
behavior = UI.GlassPane.AnchorBehavior.PreferLeft;
|
|
|
|
let arrowX;
|
|
let enoughWidth = true;
|
|
if (behavior === UI.GlassPane.AnchorBehavior.PreferLeft) {
|
|
positionX = Math.max(gutterSize, anchorBox.x - width - gutterSize);
|
|
const spaceLeft = anchorBox.x - positionX - gutterSize;
|
|
if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) {
|
|
if (width > spaceLeft) {
|
|
this._arrowElement.classList.add('arrow-none');
|
|
enoughWidth = false;
|
|
}
|
|
} else {
|
|
width = Math.min(width, spaceLeft);
|
|
}
|
|
this._arrowElement.setIconType('mediumicon-arrow-right');
|
|
this._arrowElement.classList.add('arrow-right');
|
|
arrowX = anchorBox.x - gutterSize;
|
|
} else {
|
|
positionX = anchorBox.x + anchorBox.width + gutterSize;
|
|
const spaceRight = containerWidth - positionX - gutterSize;
|
|
if (this._sizeBehavior === UI.GlassPane.SizeBehavior.MeasureContent) {
|
|
if (width > spaceRight) {
|
|
this._arrowElement.classList.add('arrow-none');
|
|
positionX = containerWidth - gutterSize - width;
|
|
enoughWidth = false;
|
|
}
|
|
} else {
|
|
width = Math.min(width, spaceRight);
|
|
}
|
|
this._arrowElement.setIconType('mediumicon-arrow-left');
|
|
this._arrowElement.classList.add('arrow-left');
|
|
arrowX = anchorBox.x + anchorBox.width + gutterSize;
|
|
}
|
|
|
|
positionY = Math.max(gutterSize, Math.min(anchorBox.y, containerHeight - height - gutterSize));
|
|
if (!enoughWidth)
|
|
positionY = Math.min(positionY + arrowSize, containerHeight - height - gutterSize);
|
|
else if (showArrow && positionY - arrowSize >= gutterSize)
|
|
positionY -= arrowSize;
|
|
height = Math.min(height, containerHeight - positionY - gutterSize);
|
|
if (2 * arrowSize >= height) {
|
|
this._arrowElement.classList.add('arrow-none');
|
|
} else {
|
|
let arrowY = anchorBox.y + Math.min(50, Math.floor(anchorBox.height / 2));
|
|
arrowY = Number.constrain(arrowY, positionY + arrowSize, positionY + height - arrowSize);
|
|
this._arrowElement.positionAt(arrowX, arrowY, container);
|
|
}
|
|
}
|
|
} 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 - gutterSize);
|
|
height = Math.min(height, containerHeight - positionY - gutterSize);
|
|
this._arrowElement.classList.add('arrow-none');
|
|
}
|
|
|
|
this.contentElement.style.width = width + 'px';
|
|
if (this._sizeBehavior === UI.GlassPane.SizeBehavior.SetExactWidthMaxHeight)
|
|
this.contentElement.style.maxHeight = height + 'px';
|
|
else
|
|
this.contentElement.style.height = height + 'px';
|
|
|
|
this.contentElement.positionAt(positionX, positionY, container);
|
|
this._widget.doResize();
|
|
}
|
|
|
|
/**
|
|
* @protected
|
|
* @return {!UI.Widget}
|
|
*/
|
|
widget() {
|
|
return this._widget;
|
|
}
|
|
|
|
/**
|
|
* @param {!Element} element
|
|
*/
|
|
static setContainer(element) {
|
|
UI.GlassPane._containers.set(/** @type {!Document} */ (element.ownerDocument), element);
|
|
UI.GlassPane.containerMoved(element);
|
|
}
|
|
|
|
/**
|
|
* @param {!Document} document
|
|
* @return {!Element}
|
|
*/
|
|
static container(document) {
|
|
return UI.GlassPane._containers.get(document);
|
|
}
|
|
|
|
/**
|
|
* @param {!Element} element
|
|
*/
|
|
static containerMoved(element) {
|
|
for (const pane of UI.GlassPane._panes) {
|
|
if (pane.isShowing() && pane.element.ownerDocument === element.ownerDocument)
|
|
pane._positionContent();
|
|
}
|
|
}
|
|
};
|
|
|
|
/** @enum {symbol} */
|
|
UI.GlassPane.PointerEventsBehavior = {
|
|
BlockedByGlassPane: Symbol('BlockedByGlassPane'),
|
|
PierceGlassPane: Symbol('PierceGlassPane'),
|
|
PierceContents: Symbol('PierceContents')
|
|
};
|
|
|
|
/** @enum {symbol} */
|
|
UI.GlassPane.AnchorBehavior = {
|
|
PreferTop: Symbol('PreferTop'),
|
|
PreferBottom: Symbol('PreferBottom'),
|
|
PreferLeft: Symbol('PreferLeft'),
|
|
PreferRight: Symbol('PreferRight'),
|
|
};
|
|
|
|
/** @enum {symbol} */
|
|
UI.GlassPane.SizeBehavior = {
|
|
SetExactSize: Symbol('SetExactSize'),
|
|
SetExactWidthMaxHeight: Symbol('SetExactWidthMaxHeight'),
|
|
MeasureContent: Symbol('MeasureContent')
|
|
};
|
|
|
|
/** @enum {symbol} */
|
|
UI.GlassPane.MarginBehavior = {
|
|
Arrow: Symbol('Arrow'),
|
|
DefaultMargin: Symbol('DefaultMargin'),
|
|
NoMargin: Symbol('NoMargin')
|
|
};
|
|
|
|
/** @type {!Map<!Document, !Element>} */
|
|
UI.GlassPane._containers = new Map();
|
|
/** @type {!Set<!UI.GlassPane>} */
|
|
UI.GlassPane._panes = new Set();
|