import type {Data} from './index';
import type {Rect} from '../utils';
import type {HostInstance} from '../../types';
import type Agent from '../../agent';
import {isReactNativeEnvironment} from 'react-devtools-shared/src/backend/utils';
const OUTLINE_COLOR = '#f0f0f0';
const COLORS = [
'#37afa9',
'#63b19e',
'#80b393',
'#97b488',
'#abb67d',
'#beb771',
'#cfb965',
'#dfba57',
'#efbb49',
'#febc38',
];
let canvas: HTMLCanvasElement | null = null;
function drawNative(nodeToData: Map<HostInstance, Data>, agent: Agent) {
const nodesToDraw = [];
iterateNodes(nodeToData, (_, color, node) => {
nodesToDraw.push({node, color});
});
agent.emit('drawTraceUpdates', nodesToDraw);
}
function drawWeb(nodeToData: Map<HostInstance, Data>) {
if (canvas === null) {
initialize();
}
const canvasFlow: HTMLCanvasElement = ((canvas: any): HTMLCanvasElement);
canvasFlow.width = window.innerWidth;
canvasFlow.height = window.innerHeight;
const context = canvasFlow.getContext('2d');
context.clearRect(0, 0, canvasFlow.width, canvasFlow.height);
iterateNodes(nodeToData, (rect, color) => {
if (rect !== null) {
drawBorder(context, rect, color);
}
});
}
export function draw(nodeToData: Map<HostInstance, Data>, agent: Agent): void {
return isReactNativeEnvironment()
? drawNative(nodeToData, agent)
: drawWeb(nodeToData);
}
function iterateNodes(
nodeToData: Map<HostInstance, Data>,
execute: (rect: Rect | null, color: string, node: HostInstance) => void,
) {
nodeToData.forEach(({count, rect}, node) => {
const colorIndex = Math.min(COLORS.length - 1, count - 1);
const color = COLORS[colorIndex];
execute(rect, color, node);
});
}
function drawBorder(
context: CanvasRenderingContext2D,
rect: Rect,
color: string,
): void {
const {height, left, top, width} = rect;
context.lineWidth = 1;
context.strokeStyle = OUTLINE_COLOR;
context.strokeRect(left - 1, top - 1, width + 2, height + 2);
context.lineWidth = 1;
context.strokeStyle = OUTLINE_COLOR;
context.strokeRect(left + 1, top + 1, width - 1, height - 1);
context.strokeStyle = color;
context.setLineDash([0]);
context.lineWidth = 1;
context.strokeRect(left, top, width - 1, height - 1);
context.setLineDash([0]);
}
function destroyNative(agent: Agent) {
agent.emit('disableTraceUpdates');
}
function destroyWeb() {
if (canvas !== null) {
if (canvas.parentNode != null) {
canvas.parentNode.removeChild(canvas);
}
canvas = null;
}
}
export function destroy(agent: Agent): void {
return isReactNativeEnvironment() ? destroyNative(agent) : destroyWeb();
}
function initialize(): void {
canvas = window.document.createElement('canvas');
canvas.style.cssText = `
xx-background-color: red;
xx-opacity: 0.5;
bottom: 0;
left: 0;
pointer-events: none;
position: fixed;
right: 0;
top: 0;
z-index: 1000000000;
`;
const root = window.document.documentElement;
root.insertBefore(canvas, root.firstChild);
}