/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-devtools-timeline/src/content-views/NetworkMeasuresView.js
337 строк
9 KB
Jan Kassens
[flow] make Flow suppressions explicit on the error (#26487)
27 мар 2023, 14:43
Не верифицирован
27 мар 2023, 14:43
afea1d0
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import type {NetworkMeasure, TimelineData} from '../types'; import type { Interaction, IntrinsicSize, MouseMoveInteraction, Rect, ViewRefs, } from '../view-base'; import { durationToWidth, positioningScaleFactor, positionToTimestamp, timestampToPosition, } from './utils/positioning'; import {drawText} from './utils/text'; import {formatDuration} from '../utils/formatting'; import { View, Surface, rectContainsPoint, rectIntersectsRect, intersectionOfRects, } from '../view-base'; import {BORDER_SIZE, COLORS, SUSPENSE_EVENT_HEIGHT} from './constants'; const HEIGHT = SUSPENSE_EVENT_HEIGHT; // TODO Constant name const ROW_WITH_BORDER_HEIGHT = HEIGHT + BORDER_SIZE; const BASE_URL_REGEX = /([^:]+:\/\/[^\/]+)/; export class NetworkMeasuresView extends View { _depthToNetworkMeasure: Map<number, NetworkMeasure[]>; _hoveredNetworkMeasure: NetworkMeasure | null = null; _intrinsicSize: IntrinsicSize; _maxDepth: number = 0; _profilerData: TimelineData; onHover: ((event: NetworkMeasure | null) => void) | null = null; constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._profilerData = profilerData; this._performPreflightComputations(); } _performPreflightComputations() { this._depthToNetworkMeasure = new Map(); const {duration, networkMeasures} = this._profilerData; networkMeasures.forEach(event => { const depth = event.depth; this._maxDepth = Math.max(this._maxDepth, depth); if (!this._depthToNetworkMeasure.has(depth)) { this._depthToNetworkMeasure.set(depth, [event]); } else { // $FlowFixMe[incompatible-use] This is unnecessary. this._depthToNetworkMeasure.get(depth).push(event); } }); this._intrinsicSize = { width: duration, height: (this._maxDepth + 1) * ROW_WITH_BORDER_HEIGHT, // Collapsed by default maxInitialHeight: 0, }; } desiredSize(): IntrinsicSize { return this._intrinsicSize; } setHoveredEvent(networkMeasure: NetworkMeasure | null) { if (this._hoveredNetworkMeasure === networkMeasure) { return; } this._hoveredNetworkMeasure = networkMeasure; this.setNeedsDisplay(); } /** * Draw a single `NetworkMeasure` as a box/span with text inside of it. */ _drawSingleNetworkMeasure( context: CanvasRenderingContext2D, networkMeasure: NetworkMeasure, baseY: number, scaleFactor: number, showHoverHighlight: boolean, ) { const {frame, visibleArea} = this; const { depth, finishTimestamp, firstReceivedDataTimestamp, lastReceivedDataTimestamp, receiveResponseTimestamp, sendRequestTimestamp, url, } = networkMeasure; // Account for requests that did not complete while we were profiling. // As well as requests that did not receive data before finish (cached?). const duration = this._profilerData.duration; const timestampBegin = sendRequestTimestamp; const timestampEnd = finishTimestamp || lastReceivedDataTimestamp || duration; const timestampMiddle = receiveResponseTimestamp || firstReceivedDataTimestamp || timestampEnd; // Convert all timestamps to x coordinates. const xStart = timestampToPosition(timestampBegin, scaleFactor, frame); const xMiddle = timestampToPosition(timestampMiddle, scaleFactor, frame); const xStop = timestampToPosition(timestampEnd, scaleFactor, frame); const width = durationToWidth(xStop - xStart, scaleFactor); if (width < 1) { return; // Too small to render at this zoom level } baseY += depth * ROW_WITH_BORDER_HEIGHT; const outerRect: Rect = { origin: { x: xStart, y: baseY, }, size: { width: xStop - xStart, height: HEIGHT, }, }; if (!rectIntersectsRect(outerRect, visibleArea)) { return; // Not in view } // Draw the secondary rect first (since it also shows as a thin border around the primary rect). let rect = { origin: { x: xStart, y: baseY, }, size: { width: xStop - xStart, height: HEIGHT, }, }; if (rectIntersectsRect(rect, visibleArea)) { context.beginPath(); context.fillStyle = this._hoveredNetworkMeasure === networkMeasure ? COLORS.NETWORK_SECONDARY_HOVER : COLORS.NETWORK_SECONDARY; context.fillRect( rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, ); } rect = { origin: { x: xStart + BORDER_SIZE, y: baseY + BORDER_SIZE, }, size: { width: xMiddle - xStart - BORDER_SIZE, height: HEIGHT - BORDER_SIZE * 2, }, }; if (rectIntersectsRect(rect, visibleArea)) { context.beginPath(); context.fillStyle = this._hoveredNetworkMeasure === networkMeasure ? COLORS.NETWORK_PRIMARY_HOVER : COLORS.NETWORK_PRIMARY; context.fillRect( rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, ); } const baseUrl = url.match(BASE_URL_REGEX); const displayUrl = baseUrl !== null ? baseUrl[1] : url; const durationLabel = finishTimestamp !== 0 ? `${formatDuration(finishTimestamp - sendRequestTimestamp)} - ` : ''; const label = durationLabel + displayUrl; drawText(label, context, outerRect, visibleArea); } draw(context: CanvasRenderingContext2D) { const { frame, _profilerData: {networkMeasures}, _hoveredNetworkMeasure, visibleArea, } = this; context.fillStyle = COLORS.PRIORITY_BACKGROUND; context.fillRect( visibleArea.origin.x, visibleArea.origin.y, visibleArea.size.width, visibleArea.size.height, ); const scaleFactor = positioningScaleFactor( this._intrinsicSize.width, frame, ); networkMeasures.forEach(networkMeasure => { this._drawSingleNetworkMeasure( context, networkMeasure, frame.origin.y, scaleFactor, networkMeasure === _hoveredNetworkMeasure, ); }); // Render bottom borders. for (let i = 0; i <= this._maxDepth; i++) { const borderFrame: Rect = { origin: { x: frame.origin.x, y: frame.origin.y + (i + 1) * ROW_WITH_BORDER_HEIGHT - BORDER_SIZE, }, size: { width: frame.size.width, height: BORDER_SIZE, }, }; if (rectIntersectsRect(borderFrame, visibleArea)) { const borderDrawableRect = intersectionOfRects( borderFrame, visibleArea, ); context.fillStyle = COLORS.PRIORITY_BORDER; context.fillRect( borderDrawableRect.origin.x, borderDrawableRect.origin.y, borderDrawableRect.size.width, borderDrawableRect.size.height, ); } } } /** * @private */ _handleMouseMove(interaction: MouseMoveInteraction, viewRefs: ViewRefs) { const {frame, _intrinsicSize, onHover, visibleArea} = this; if (!onHover) { return; } const {location} = interaction.payload; if (!rectContainsPoint(location, visibleArea)) { onHover(null); return; } const scaleFactor = positioningScaleFactor(_intrinsicSize.width, frame); const hoverTimestamp = positionToTimestamp(location.x, scaleFactor, frame); const adjustedCanvasMouseY = location.y - frame.origin.y; const depth = Math.floor(adjustedCanvasMouseY / ROW_WITH_BORDER_HEIGHT); const networkMeasuresAtDepth = this._depthToNetworkMeasure.get(depth); const duration = this._profilerData.duration; if (networkMeasuresAtDepth) { // Find the event being hovered over. for (let index = networkMeasuresAtDepth.length - 1; index >= 0; index--) { const networkMeasure = networkMeasuresAtDepth[index]; const { finishTimestamp, lastReceivedDataTimestamp, sendRequestTimestamp, } = networkMeasure; const timestampBegin = sendRequestTimestamp; const timestampEnd = finishTimestamp || lastReceivedDataTimestamp || duration; if ( hoverTimestamp >= timestampBegin && hoverTimestamp <= timestampEnd ) { this.currentCursor = 'context-menu'; viewRefs.hoveredView = this; onHover(networkMeasure); return; } } } if (viewRefs.hoveredView === this) { viewRefs.hoveredView = null; } onHover(null); } handleInteraction(interaction: Interaction, viewRefs: ViewRefs) { switch (interaction.type) { case 'mousemove': this._handleMouseMove(interaction, viewRefs); break; } } }