/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-devtools-timeline/src/content-views/FlamechartView.js
383 строки
11 KB
Andrew Clark
[Codemod] Update copyright header to Meta (#25315)
18 окт 2022, 18:19
Не верифицирован
18 окт 2022, 18:19
9cdf8a9
Код
Авторство
О чём код?
/** * 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 { Flamechart, FlamechartStackFrame, FlamechartStackLayer, InternalModuleSourceToRanges, } from '../types'; import type { Interaction, MouseMoveInteraction, Rect, Size, ViewRefs, } from '../view-base'; import { BackgroundColorView, Surface, View, layeredLayout, rectContainsPoint, intersectionOfRects, rectIntersectsRect, verticallyStackedLayout, } from '../view-base'; import {isInternalModule} from './utils/moduleFilters'; import { durationToWidth, positioningScaleFactor, timestampToPosition, } from './utils/positioning'; import {drawText} from './utils/text'; import { COLORS, FLAMECHART_FRAME_HEIGHT, COLOR_HOVER_DIM_DELTA, BORDER_SIZE, } from './constants'; import {ColorGenerator, dimmedColor, hslaColorToString} from './utils/colors'; // Source: https://source.chromium.org/chromium/chromium/src/+/master:out/Debug/gen/devtools/timeline/TimelineUIUtils.js;l=2109;drc=fb32e928d79707a693351b806b8710b2f6b7d399 const colorGenerator = new ColorGenerator( {min: 30, max: 330}, {min: 50, max: 80, count: 3}, 85, ); colorGenerator.setColorForID('', {h: 43.6, s: 45.8, l: 90.6, a: 100}); function defaultHslaColorForStackFrame({scriptUrl}: FlamechartStackFrame) { return colorGenerator.colorForID(scriptUrl ?? ''); } function defaultColorForStackFrame(stackFrame: FlamechartStackFrame): string { const color = defaultHslaColorForStackFrame(stackFrame); return hslaColorToString(color); } function hoverColorForStackFrame(stackFrame: FlamechartStackFrame): string { const color = dimmedColor( defaultHslaColorForStackFrame(stackFrame), COLOR_HOVER_DIM_DELTA, ); return hslaColorToString(color); } class FlamechartStackLayerView extends View { /** Layer to display */ _stackLayer: FlamechartStackLayer; /** A set of `stackLayer`'s frames, for efficient lookup. */ _stackFrameSet: Set<FlamechartStackFrame>; _internalModuleSourceToRanges: InternalModuleSourceToRanges; _intrinsicSize: Size; _hoveredStackFrame: FlamechartStackFrame | null = null; _onHover: ((node: FlamechartStackFrame | null) => void) | null = null; constructor( surface: Surface, frame: Rect, stackLayer: FlamechartStackLayer, internalModuleSourceToRanges: InternalModuleSourceToRanges, duration: number, ) { super(surface, frame); this._stackLayer = stackLayer; this._stackFrameSet = new Set(stackLayer); this._internalModuleSourceToRanges = internalModuleSourceToRanges; this._intrinsicSize = { width: duration, height: FLAMECHART_FRAME_HEIGHT, }; } desiredSize(): Size { return this._intrinsicSize; } setHoveredFlamechartStackFrame( hoveredStackFrame: FlamechartStackFrame | null, ) { if (this._hoveredStackFrame === hoveredStackFrame) { return; // We're already hovering over this frame } // Only care about frames displayed by this view. const stackFrameToSet = hoveredStackFrame && this._stackFrameSet.has(hoveredStackFrame) ? hoveredStackFrame : null; if (this._hoveredStackFrame === stackFrameToSet) { return; // Resulting state is unchanged } this._hoveredStackFrame = stackFrameToSet; this.setNeedsDisplay(); } draw(context: CanvasRenderingContext2D) { const { frame, _stackLayer, _hoveredStackFrame, _intrinsicSize, visibleArea, } = this; context.fillStyle = COLORS.PRIORITY_BACKGROUND; context.fillRect( visibleArea.origin.x, visibleArea.origin.y, visibleArea.size.width, visibleArea.size.height, ); const scaleFactor = positioningScaleFactor(_intrinsicSize.width, frame); for (let i = 0; i < _stackLayer.length; i++) { const stackFrame = _stackLayer[i]; const {name, timestamp, duration} = stackFrame; const width = durationToWidth(duration, scaleFactor); if (width < 1) { continue; // Too small to render at this zoom level } const x = Math.floor(timestampToPosition(timestamp, scaleFactor, frame)); const nodeRect: Rect = { origin: {x, y: frame.origin.y}, size: { width: Math.floor(width - BORDER_SIZE), height: Math.floor(FLAMECHART_FRAME_HEIGHT - BORDER_SIZE), }, }; if (!rectIntersectsRect(nodeRect, visibleArea)) { continue; // Not in view } const showHoverHighlight = _hoveredStackFrame === _stackLayer[i]; let textFillStyle; if (isInternalModule(this._internalModuleSourceToRanges, stackFrame)) { context.fillStyle = showHoverHighlight ? COLORS.INTERNAL_MODULE_FRAME_HOVER : COLORS.INTERNAL_MODULE_FRAME; textFillStyle = COLORS.INTERNAL_MODULE_FRAME_TEXT; } else { context.fillStyle = showHoverHighlight ? hoverColorForStackFrame(stackFrame) : defaultColorForStackFrame(stackFrame); textFillStyle = COLORS.TEXT_COLOR; } const drawableRect = intersectionOfRects(nodeRect, visibleArea); context.fillRect( drawableRect.origin.x, drawableRect.origin.y, drawableRect.size.width, drawableRect.size.height, ); drawText(name, context, nodeRect, drawableRect, { fillStyle: textFillStyle, }); } // Render bottom border. const borderFrame: Rect = { origin: { x: frame.origin.x, y: frame.origin.y + FLAMECHART_FRAME_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 {_stackLayer, frame, _intrinsicSize, _onHover, visibleArea} = this; const {location} = interaction.payload; if (!_onHover || !rectContainsPoint(location, visibleArea)) { return; } // Find the node being hovered over. const scaleFactor = positioningScaleFactor(_intrinsicSize.width, frame); let startIndex = 0; let stopIndex = _stackLayer.length - 1; while (startIndex <= stopIndex) { const currentIndex = Math.floor((startIndex + stopIndex) / 2); const flamechartStackFrame = _stackLayer[currentIndex]; const {timestamp, duration} = flamechartStackFrame; const x = Math.floor(timestampToPosition(timestamp, scaleFactor, frame)); const width = durationToWidth(duration, scaleFactor); // Don't show tooltips for nodes that are too small to render at this zoom level. if (Math.floor(width - BORDER_SIZE) >= 1) { if (x <= location.x && x + width >= location.x) { this.currentCursor = 'context-menu'; viewRefs.hoveredView = this; _onHover(flamechartStackFrame); return; } } if (x > location.x) { stopIndex = currentIndex - 1; } else { startIndex = currentIndex + 1; } } _onHover(null); } _didGrab: boolean = false; handleInteraction(interaction: Interaction, viewRefs: ViewRefs) { switch (interaction.type) { case 'mousemove': this._handleMouseMove(interaction, viewRefs); break; } } } export class FlamechartView extends View { _flamechartRowViews: FlamechartStackLayerView[] = []; /** Container view that vertically stacks flamechart rows */ _verticalStackView: View; _hoveredStackFrame: FlamechartStackFrame | null = null; _onHover: ((node: FlamechartStackFrame | null) => void) | null = null; constructor( surface: Surface, frame: Rect, flamechart: Flamechart, internalModuleSourceToRanges: InternalModuleSourceToRanges, duration: number, ) { super(surface, frame, layeredLayout); this.setDataAndUpdateSubviews( flamechart, internalModuleSourceToRanges, duration, ); } setDataAndUpdateSubviews( flamechart: Flamechart, internalModuleSourceToRanges: InternalModuleSourceToRanges, duration: number, ) { const {surface, frame, _onHover, _hoveredStackFrame} = this; // Clear existing rows on data update if (this._verticalStackView) { this.removeAllSubviews(); this._flamechartRowViews = []; } this._verticalStackView = new View(surface, frame, verticallyStackedLayout); this._flamechartRowViews = flamechart.map(stackLayer => { const rowView = new FlamechartStackLayerView( surface, frame, stackLayer, internalModuleSourceToRanges, duration, ); this._verticalStackView.addSubview(rowView); // Update states rowView._onHover = _onHover; rowView.setHoveredFlamechartStackFrame(_hoveredStackFrame); return rowView; }); // Add a plain background view to prevent gaps from appearing between flamechartRowViews. this.addSubview(new BackgroundColorView(surface, frame)); this.addSubview(this._verticalStackView); } setHoveredFlamechartStackFrame( hoveredStackFrame: FlamechartStackFrame | null, ) { this._hoveredStackFrame = hoveredStackFrame; this._flamechartRowViews.forEach(rowView => rowView.setHoveredFlamechartStackFrame(hoveredStackFrame), ); } setOnHover(onHover: (node: FlamechartStackFrame | null) => void) { this._onHover = onHover; this._flamechartRowViews.forEach(rowView => (rowView._onHover = onHover)); } desiredSize(): { height: number, hideScrollBarIfLessThanHeight?: number, maxInitialHeight?: number, width: number, } { // Ignore the wishes of the background color view const intrinsicSize = this._verticalStackView.desiredSize(); return { ...intrinsicSize, // Collapsed by default maxInitialHeight: 0, }; } /** * @private */ _handleMouseMove(interaction: MouseMoveInteraction) { const {_onHover, visibleArea} = this; if (!_onHover) { return; } const {location} = interaction.payload; if (!rectContainsPoint(location, visibleArea)) { // Clear out any hovered flamechart stack frame _onHover(null); } } handleInteraction(interaction: Interaction) { switch (interaction.type) { case 'mousemove': this._handleMouseMove(interaction); break; } } }