UNPKG

react-dev-inspector

Version:

dev-tool for inspect react components and jump to local IDE for component code.

364 lines (363 loc) 13.3 kB
"use strict"; /** * mirror from https://github.com/facebook/react/blob/v16.13.1/packages/react-devtools-shared/src/backend/views/utils.js */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Overlay = void 0; const utils_1 = require("./utils"); // Note that the Overlay components are not affected by the active Theme, // because they highlight elements in the main Chrome window (outside of devtools). // The colors below were chosen to roughly match those used by Chrome devtools. class OverlayRect { constructor(doc, container) { Object.defineProperty(this, "node", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "border", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "padding", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "content", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.node = doc.createElement('div'); this.border = doc.createElement('div'); this.padding = doc.createElement('div'); this.content = doc.createElement('div'); this.border.style.borderColor = overlayStyles.border; this.padding.style.borderColor = overlayStyles.padding; this.content.style.backgroundColor = overlayStyles.background; Object.assign(this.node.style, { borderColor: overlayStyles.margin, pointerEvents: 'none', position: 'fixed', }); this.node.style.zIndex = '10000000'; this.node.appendChild(this.border); this.border.appendChild(this.padding); this.padding.appendChild(this.content); // ensure OverlayRect dom always before OverlayTip dom rather than cover OverlayTip container.prepend(this.node); } remove() { if (this.node.parentNode) { this.node.parentNode.removeChild(this.node); } } update(box, dims) { boxWrap(dims, 'margin', this.node); boxWrap(dims, 'border', this.border); boxWrap(dims, 'padding', this.padding); Object.assign(this.content.style, { height: `${box.height - dims.borderTop - dims.borderBottom - dims.paddingTop - dims.paddingBottom}px`, width: `${box.width - dims.borderLeft - dims.borderRight - dims.paddingLeft - dims.paddingRight}px`, }); Object.assign(this.node.style, { top: `${box.top - dims.marginTop}px`, left: `${box.left - dims.marginLeft}px`, }); } } class OverlayTip { constructor(doc, container) { Object.defineProperty(this, "tip", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "nameSpan", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "titleDiv", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "infoDiv", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "dimSpan", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.tip = doc.createElement('div'); Object.assign(this.tip.style, { display: 'flex', flexFlow: 'row nowrap', alignItems: 'center', backgroundColor: '#333740', borderRadius: '2px', fontFamily: '"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace', fontWeight: 'bold', padding: '6px 8px', pointerEvents: 'none', position: 'fixed', fontSize: '12px', whiteSpace: 'nowrap', }); this.nameSpan = doc.createElement('span'); this.tip.appendChild(this.nameSpan); Object.assign(this.nameSpan.style, { display: 'flex', flexDirection: 'column', borderRight: '1px solid #aaaaaa', paddingRight: '0.8rem', marginRight: '0.8rem', }); this.titleDiv = doc.createElement('div'); this.nameSpan.appendChild(this.titleDiv); Object.assign(this.titleDiv.style, { color: '#ee78e6', fontSize: '16px', }); this.infoDiv = doc.createElement('div'); this.nameSpan.appendChild(this.infoDiv); Object.assign(this.infoDiv.style, { color: '#ee78e6', fontSize: '14px', }); this.dimSpan = doc.createElement('span'); this.tip.appendChild(this.dimSpan); Object.assign(this.dimSpan.style, { color: '#d7d7d7', }); this.tip.style.zIndex = '10000000'; container.appendChild(this.tip); } remove() { if (this.tip.parentNode) { this.tip.parentNode.removeChild(this.tip); } } updateText(name, info, width, height) { this.titleDiv.textContent = name; this.infoDiv.textContent = info !== null && info !== void 0 ? info : ''; this.dimSpan.textContent = `${Math.round(width)}px × ${Math.round(height)}px`; } updatePosition(dims, bounds) { const tipRect = this.tip.getBoundingClientRect(); const tipPos = findTipPos(dims, bounds, { width: tipRect.width, height: tipRect.height, }); Object.assign(this.tip.style, tipPos.style); } } class Overlay { constructor() { Object.defineProperty(this, "window", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "tipBoundsWindow", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "container", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "tip", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "rects", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "removeCallback", { enumerable: true, configurable: true, writable: true, value: void 0 }); // Find the root window, because overlays are positioned relative to it. const currentWindow = window.__REACT_DEVTOOLS_TARGET_WINDOW__ || window; this.window = currentWindow; // When opened in shells/dev, // the tooltip should be bound by the app iframe, not by the topmost window. const tipBoundsWindow = window.__REACT_DEVTOOLS_TARGET_WINDOW__ || window; this.tipBoundsWindow = tipBoundsWindow; const doc = currentWindow.document; this.container = doc.createElement('div'); this.container.style.zIndex = '10000000'; this.tip = new OverlayTip(doc, this.container); this.rects = []; this.removeCallback = () => { }; doc.body.appendChild(this.container); } remove() { this.tip.remove(); this.rects.forEach(rect => { rect.remove(); }); this.rects.length = 0; if (this.container.parentNode) { this.container.parentNode.removeChild(this.container); } this.removeCallback(); } setRemoveCallback(callback) { this.removeCallback = callback.bind(this); } inspect(nodes, name, info) { var _a; // We can't get the size of text nodes or comment nodes. React as of v15 // heavily uses comment nodes to delimit text. const elements = nodes.filter(node => node.nodeType === Node.ELEMENT_NODE); while (this.rects.length > elements.length) { const rect = this.rects.pop(); rect === null || rect === void 0 ? void 0 : rect.remove(); } if (elements.length === 0) { return; } while (this.rects.length < elements.length) { this.rects.push(new OverlayRect(this.window.document, this.container)); } const outerBox = { top: Number.POSITIVE_INFINITY, right: Number.NEGATIVE_INFINITY, bottom: Number.NEGATIVE_INFINITY, left: Number.POSITIVE_INFINITY, }; elements.forEach((element, index) => { const box = (0, utils_1.getNestedBoundingClientRect)(element, this.window); const dims = (0, utils_1.getElementDimensions)(element); outerBox.top = Math.min(outerBox.top, box.top - dims.marginTop); outerBox.right = Math.max(outerBox.right, box.left + box.width + dims.marginRight); outerBox.bottom = Math.max(outerBox.bottom, box.top + box.height + dims.marginBottom); outerBox.left = Math.min(outerBox.left, box.left - dims.marginLeft); const rect = this.rects[index]; rect.update(box, dims); }); if (!name) { name = elements[0].nodeName.toLowerCase(); const node = elements[0]; const hook = (_a = node.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.__REACT_DEVTOOLS_GLOBAL_HOOK__; if (hook === null || hook === void 0 ? void 0 : hook.rendererInterfaces) { let ownerName = null; for (const rendererInterface of hook.rendererInterfaces.values()) { const id = rendererInterface.getFiberIDForNative(node, true); if (id !== null) { ownerName = rendererInterface.getDisplayNameForFiberID(id, true); break; } } if (ownerName) { name += ` (in ${ownerName})`; } } } this.tip.updateText(name, info, outerBox.right - outerBox.left, outerBox.bottom - outerBox.top); const tipBounds = (0, utils_1.getNestedBoundingClientRect)(this.tipBoundsWindow.document.documentElement, this.window); this.tip.updatePosition({ top: outerBox.top, left: outerBox.left, height: outerBox.bottom - outerBox.top, width: outerBox.right - outerBox.left, }, { top: tipBounds.top + this.tipBoundsWindow.scrollY, left: tipBounds.left + this.tipBoundsWindow.scrollX, height: this.tipBoundsWindow.innerHeight, width: this.tipBoundsWindow.innerWidth, }); } } exports.Overlay = Overlay; function findTipPos(dims, bounds, tipSize) { const tipHeight = Math.max(tipSize.height, 20); const tipWidth = Math.max(tipSize.width, 60); const margin = 5; let top; if (dims.top + dims.height + tipHeight <= bounds.top + bounds.height) { if (dims.top + dims.height < bounds.top + 0) { top = bounds.top + margin; } else { top = dims.top + dims.height + margin; } } else if (dims.top - tipHeight <= bounds.top + bounds.height) { if (dims.top - tipHeight - margin < bounds.top + margin) { top = bounds.top + margin; } else { top = dims.top - tipHeight - margin; } } else { top = bounds.top + bounds.height - tipHeight - margin; } let left = dims.left + margin; if (dims.left < bounds.left) { left = bounds.left + margin; } if (dims.left + tipWidth > bounds.left + bounds.width) { left = bounds.left + bounds.width - tipWidth - margin; } return { style: { top: `${top}px`, left: `${left}px`, }, }; } function boxWrap(dims, what, node) { Object.assign(node.style, { borderTopWidth: `${dims[`${what}Top`]}px`, borderLeftWidth: `${dims[`${what}Left`]}px`, borderRightWidth: `${dims[`${what}Right`]}px`, borderBottomWidth: `${dims[`${what}Bottom`]}px`, borderStyle: 'solid', }); } const overlayStyles = { background: 'rgba(120, 170, 210, 0.7)', padding: 'rgba(77, 200, 0, 0.3)', margin: 'rgba(255, 155, 0, 0.3)', border: 'rgba(255, 200, 50, 0.3)', };