From f48c8e9843c4eac64bf7186e4053724b64a10856 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sun, 10 Jul 2022 09:38:38 +0200 Subject: [PATCH 1/2] Simplify highlighter code --- src/adapter/dom.ts | 85 ++++++++++++----------------- src/view/components/Highlighter.tsx | 79 +++++++++++---------------- 2 files changed, 68 insertions(+), 96 deletions(-) diff --git a/src/adapter/dom.ts b/src/adapter/dom.ts index f58d672a..6f8af41b 100644 --- a/src/adapter/dom.ts +++ b/src/adapter/dom.ts @@ -8,30 +8,21 @@ export function px2Int(input: string | null) { return input ? +input.replace(/px/, "") : 0; } +/** Top, Right, Bottom, Left */ +export type Dimensions = [number, number, number, number]; +/** Top, Right, Bottom, Left */ +export type Bounds = [boolean, boolean, boolean, boolean]; + export interface Measurements { boxSizing: string; top: number; left: number; width: number; height: number; - marginTop: number; - marginRight: number; - marginBottom: number; - marginLeft: number; - borderTop: number; - borderRight: number; - borderBottom: number; - borderLeft: number; - paddingTop: number; - paddingRight: number; - paddingBottom: number; - paddingLeft: number; - bounds: { - top?: boolean; - left?: boolean; - bottom?: boolean; - right?: boolean; - }; + margin: Dimensions; + border: Dimensions; + padding: Dimensions; + bounds: Bounds; } function getBoundsState(rect: { @@ -39,13 +30,12 @@ function getBoundsState(rect: { height: number; left: number; width: number; -}) { - return { - top: rect.top + window.pageYOffset < window.scrollY, - bottom: rect.top + rect.height > window.innerHeight + scrollY, - left: rect.left + window.pageXOffset < window.scrollX, - right: rect.left + rect.width > window.scrollX + window.innerWidth, - }; +}): Bounds { + const top = rect.top + window.pageYOffset < window.scrollY; + const bottom = rect.top + rect.height > window.innerHeight + scrollY; + const left = rect.left + window.pageXOffset < window.scrollX; + const right = rect.left + rect.width > window.scrollX + window.innerWidth; + return [top, right, bottom, left]; } export function measureNode(dom: Element): Measurements { @@ -66,18 +56,24 @@ export function measureNode(dom: Element): Measurements { width: Math.round(r.width * 100) / 100, height: Math.round(r.height * 100) / 100, - marginTop: px2Int(s.marginTop), - marginRight: px2Int(s.marginRight), - marginBottom: px2Int(s.marginBottom), - marginLeft: px2Int(s.marginLeft), - borderTop: px2Int(s.borderTopWidth), - borderRight: px2Int(s.borderRightWidth), - borderBottom: px2Int(s.borderBottomWidth), - borderLeft: px2Int(s.borderLeftWidth), - paddingTop: px2Int(s.paddingTop), - paddingRight: px2Int(s.paddingRight), - paddingBottom: px2Int(s.paddingBottom), - paddingLeft: px2Int(s.paddingLeft), + margin: [ + px2Int(s.marginTop), + px2Int(s.marginRight), + px2Int(s.marginBottom), + px2Int(s.marginLeft), + ], + border: [ + px2Int(s.borderTopWidth), + px2Int(s.borderRightWidth), + px2Int(s.borderBottomWidth), + px2Int(s.borderLeftWidth), + ], + padding: [ + px2Int(s.paddingTop), + px2Int(s.paddingRight), + px2Int(s.paddingBottom), + px2Int(s.paddingLeft), + ], }; } @@ -101,17 +97,8 @@ export function mergeMeasure(a: Measurements, b: Measurements): Measurements { // Reset all margins for combined nodes. There is no // meaningful way to display them. - marginTop: 0, - marginRight: 0, - marginBottom: 0, - marginLeft: 0, - borderTop: 0, - borderRight: 0, - borderBottom: 0, - borderLeft: 0, - paddingTop: 0, - paddingRight: 0, - paddingBottom: 0, - paddingLeft: 0, + margin: [0, 0, 0, 0], + border: [0, 0, 0, 0], + padding: [0, 0, 0, 0], }; } diff --git a/src/view/components/Highlighter.tsx b/src/view/components/Highlighter.tsx index 99e70e43..81806109 100644 --- a/src/view/components/Highlighter.tsx +++ b/src/view/components/Highlighter.tsx @@ -1,18 +1,13 @@ import { h } from "preact"; import s from "./Highlighter.module.css"; -import { Measurements } from "../../adapter/dom"; +import { Dimensions, Measurements } from "../../adapter/dom"; -export function css2Border( - top: number, - right: number, - bottom: number, - left: number, -) { +export function css2Border(dim: Dimensions) { return ` - border-top-width: ${top}px; - border-right-width: ${right}px; - border-bottom-width: ${bottom}px; - border-left-width: ${left}px; + border-top-width: ${dim[0]}px; + border-right-width: ${dim[1]}px; + border-bottom-width: ${dim[2]}px; + border-left-width: ${dim[3]}px; `; } @@ -23,10 +18,26 @@ export interface Props extends Measurements { } export function Highlighter(props: Props) { - const { width, height, boxSizing, top, left, bounds } = props; + const { + width, + height, + boxSizing, + top, + left, + bounds, + padding, + margin, + } = props; - const isOutOfBounds = - bounds.bottom || bounds.left || bounds.right || bounds.top; + const isOutOfBounds = bounds[0] || bounds[1] || bounds[2] || bounds[3]; + + let contentBoxCss = ""; + if (boxSizing === "content-box") { + const diffHeight = padding[0] + padding[2]; + const diffWidth = padding[1] + padding[3]; + contentBoxCss += `height: calc(100% - ${diffHeight}px);`; + contentBoxCss += `width: calc(100% - ${diffWidth}px);`; + } return (
-
+
{props.label} |{" "} {width}px ×{" "} From f5de0adddd9b97bab2bdf5519123421eff194f58 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sun, 10 Jul 2022 10:32:06 +0200 Subject: [PATCH 2/2] Drop Preact from content-script --- src/adapter/adapter/highlight.ts | 38 ++--- src/adapter/adapter/highlightUpdates.ts | 22 +-- src/adapter/adapter/port.ts | 2 +- src/adapter/adapter/profiler.ts | 2 +- src/adapter/hook.ts | 2 +- src/adapter/shared/renderer.ts | 10 +- src/adapter/shared/stats.ts | 2 +- src/adapter/shared/utils.ts | 2 +- .../CanvasHighlight/CanvasHighlight.tsx | 57 +++++--- src/view/components/Highlighter.tsx | 132 +++++++++++------- 10 files changed, 150 insertions(+), 119 deletions(-) diff --git a/src/adapter/adapter/highlight.ts b/src/adapter/adapter/highlight.ts index f9cce9c5..6643b0a1 100644 --- a/src/adapter/adapter/highlight.ts +++ b/src/adapter/adapter/highlight.ts @@ -1,5 +1,4 @@ import { Renderer } from "../renderer"; -import { render, h } from "preact"; import { getNearestElement, measureNode, mergeMeasure } from "../dom"; import { ID } from "../../view/store/types"; import { Highlighter, style } from "../../view/components/Highlighter"; @@ -18,23 +17,17 @@ export function createHightlighter( * hovering a node in the tree. */ let highlightRef: HTMLDivElement | null = null; - - function destroyHighlight() { - if (highlightRef) { - document.body.removeChild(highlightRef!); - } - highlightRef = null; - } + const highlighter = new Highlighter(); function highlight(id: ID) { const renderer = getRendererByVnodeId(id); if (!renderer) { - return destroyHighlight(); + return highlighter.destroy(); } const vnode = renderer.getVNodeById(id); if (!vnode) { - return destroyHighlight(); + return highlighter.destroy(); } const dom = renderer.findDomForVNode(id); @@ -94,24 +87,21 @@ export function createHightlighter( let height = size.height; let width = size.width; if (size.boxSizing === "border-box") { - height += size.marginTop + size.marginBottom; - width += size.marginLeft + size.marginRight; + height += size.margin[0] + size.margin[2]; + width += size.margin[1] + size.margin[3]; } - render( - h(Highlighter, { - label, - ...size, - top: size.top - size.marginTop, - left: size.left - size.marginLeft, - height, - width, - }), - highlightRef, - ); + highlighter.render({ + label, + ...size, + top: size.top - size.margin[0], + left: size.left - size.margin[3], + height, + width, + }); } } } - return { highlight, destroy: destroyHighlight }; + return { highlight, destroy: () => highlighter.destroy() }; } diff --git a/src/adapter/adapter/highlightUpdates.ts b/src/adapter/adapter/highlightUpdates.ts index 7d1ee31f..d8c01652 100644 --- a/src/adapter/adapter/highlightUpdates.ts +++ b/src/adapter/adapter/highlightUpdates.ts @@ -1,4 +1,3 @@ -import { render, h } from "preact"; import { CanvasHighlight } from "../../view/components/CanvasHighlight/CanvasHighlight"; const DISPLAY_DURATION = 250; @@ -72,18 +71,14 @@ export function drawRect(ctx: CanvasRenderingContext2D, data: UpdateRect) { let timer: NodeJS.Timeout; -let container: HTMLDivElement | null = null; -let canvas: HTMLCanvasElement | null = null; +const component = new CanvasHighlight(); + export function destroyCanvas() { - if (container) { - render(null, container); - container.remove(); - container = null; - canvas = null; - } + component.destroy(); } function draw(updates: UpdateRects) { + const canvas = component.canvas; if (!canvas || !canvas.getContext) return; if (timer) clearTimeout(timer); @@ -111,13 +106,6 @@ function draw(updates: UpdateRects) { } export function startDrawing(updateRects: UpdateRects) { - if (!canvas) { - container = document.createElement("div"); - container.id = "preact-devtools-highlight-updates"; - document.body.appendChild(container); - - render(h(CanvasHighlight, null), container); - canvas = container.querySelector("canvas")!; - } + component.render(); draw(updateRects); } diff --git a/src/adapter/adapter/port.ts b/src/adapter/adapter/port.ts index 893196ce..29debba3 100644 --- a/src/adapter/adapter/port.ts +++ b/src/adapter/adapter/port.ts @@ -1,4 +1,4 @@ -import { DevtoolEvents } from "../hook"; +import type { DevtoolEvents } from "../hook"; import { DevtoolsToClient, PageHookName } from "../../constants"; export interface BaseEvent { diff --git a/src/adapter/adapter/profiler.ts b/src/adapter/adapter/profiler.ts index c89cd637..7262f627 100644 --- a/src/adapter/adapter/profiler.ts +++ b/src/adapter/adapter/profiler.ts @@ -1,4 +1,4 @@ -import { UpdateRects } from "./highlightUpdates"; +import type { UpdateRects } from "./highlightUpdates"; export interface ProfilerState { isProfiling: boolean; diff --git a/src/adapter/hook.ts b/src/adapter/hook.ts index c5f89e38..6e1c233c 100644 --- a/src/adapter/hook.ts +++ b/src/adapter/hook.ts @@ -2,7 +2,7 @@ import { Renderer } from "./renderer"; import { ID } from "../view/store/types"; import { createAdapter, InspectData, UpdateType } from "./adapter/adapter"; import { DEFAULT_FIlTERS, FilterState, RawFilterState } from "./adapter/filter"; -import { Options } from "preact"; +import type { Options } from "preact"; import { createRenderer, RendererConfig } from "./shared/renderer"; import { setupOptionsV10 } from "./10/options"; import parseSemverish from "./parse-semverish"; diff --git a/src/adapter/shared/renderer.ts b/src/adapter/shared/renderer.ts index 1abca2fe..347277a8 100644 --- a/src/adapter/shared/renderer.ts +++ b/src/adapter/shared/renderer.ts @@ -1,10 +1,14 @@ import { BaseEvent, PortPageHook } from "../adapter/port"; import { Commit, flush } from "../protocol/events"; -import { FunctionalComponent, ComponentConstructor, Options } from "preact"; +import type { + FunctionalComponent, + ComponentConstructor, + Options, +} from "preact"; import { ID, DevNodeType } from "../../view/store/types"; import { traverse } from "./utils"; -import { FilterState } from "../adapter/filter"; -import { Renderer } from "../renderer"; +import type { FilterState } from "../adapter/filter"; +import type { Renderer } from "../renderer"; import { startDrawing } from "../adapter/highlightUpdates"; import { setIn, setInCopy } from "../shared/serialize"; import { createStats, OperationInfo } from "../shared/stats"; diff --git a/src/adapter/shared/stats.ts b/src/adapter/shared/stats.ts index 2dcabdd9..3f3f4ec7 100644 --- a/src/adapter/shared/stats.ts +++ b/src/adapter/shared/stats.ts @@ -1,6 +1,6 @@ import { DevNodeType } from "../../view/store/types"; import { MsgTypes } from "../protocol/events"; -import { PreactBindings, SharedVNode } from "./bindings"; +import type { PreactBindings, SharedVNode } from "./bindings"; import { getDevtoolsType, RendererConfig } from "./renderer"; export enum DiffType { diff --git a/src/adapter/shared/utils.ts b/src/adapter/shared/utils.ts index 9a2e756f..1cd2c172 100644 --- a/src/adapter/shared/utils.ts +++ b/src/adapter/shared/utils.ts @@ -1,4 +1,4 @@ -import { PreactBindings, SharedVNode } from "./bindings"; +import type { PreactBindings, SharedVNode } from "./bindings"; export function traverse( vnode: T, diff --git a/src/view/components/CanvasHighlight/CanvasHighlight.tsx b/src/view/components/CanvasHighlight/CanvasHighlight.tsx index bfbc7382..b4f0740e 100644 --- a/src/view/components/CanvasHighlight/CanvasHighlight.tsx +++ b/src/view/components/CanvasHighlight/CanvasHighlight.tsx @@ -1,24 +1,43 @@ -import { h } from "preact"; -import { useRef } from "preact/hooks"; -import { useResize } from "../utils"; +import { throttle } from "../../../shells/shared/utils"; import s from "./CanvasHighlight.module.css"; -export function CanvasHighlight() { - const ref = useRef(); +export class CanvasHighlight { + dom: HTMLDivElement | null = null; + canvas: HTMLCanvasElement | null = null; + listener: any = null; - useResize(() => { - if (ref.current) { - ref.current.width = window.innerWidth; - ref.current.height = window.innerHeight; + id = "preact-devtools-highlight-updates"; + + mount() { + document.getElementById(this.id)?.remove(); + const dom = document.createElement("div"); + dom.id = this.id; + document.body.appendChild(dom); + + const canvas = document.createElement("canvas"); + canvas.className = s.root; + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + dom.appendChild(canvas); + this.canvas = canvas; + + window.addEventListener("resize", this.onResize); + } + + onResize = throttle(() => { + if (!this.canvas) return; + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + }, 60); + + destroy() { + window.removeEventListener("resize", this.onResize); + this.dom?.remove(); + } + + render() { + if (!this.dom) { + this.mount(); } - }, []); - - return ( - - ); + } } diff --git a/src/view/components/Highlighter.tsx b/src/view/components/Highlighter.tsx index 81806109..ffa4f9d5 100644 --- a/src/view/components/Highlighter.tsx +++ b/src/view/components/Highlighter.tsx @@ -1,4 +1,3 @@ -import { h } from "preact"; import s from "./Highlighter.module.css"; import { Dimensions, Measurements } from "../../adapter/dom"; @@ -17,56 +16,87 @@ export interface Props extends Measurements { label: string; } -export function Highlighter(props: Props) { - const { - width, - height, - boxSizing, - top, - left, - bounds, - padding, - margin, - } = props; - - const isOutOfBounds = bounds[0] || bounds[1] || bounds[2] || bounds[3]; - - let contentBoxCss = ""; - if (boxSizing === "content-box") { - const diffHeight = padding[0] + padding[2]; - const diffWidth = padding[1] + padding[3]; - contentBoxCss += `height: calc(100% - ${diffHeight}px);`; - contentBoxCss += `width: calc(100% - ${diffWidth}px);`; +function template(html: string) { + const tpl = document.createElement("template"); + tpl.innerHTML = html; + return tpl.content.firstChild!; +} + +const dom = template( + `
| ×
`, +); + +export class Highlighter { + private id = "preact-devtools-highlighter"; + private dom: HTMLDivElement | null = null; + + mount() { + const root = document.createElement("div"); + root.id = this.id; + root.className = style.outerContainer; + document.body.appendChild(root); + + this.dom = dom.cloneNode(true) as HTMLDivElement; + root.appendChild(this.dom); } - return ( -
-
-
-
-
-
- - {props.label} |{" "} - {width}px ×{" "} - {height}px - -
- ); + destroy() { + document.getElementById(this.id)?.remove(); + } + + render(props: Props) { + if (!this.dom) { + this.mount(); + } + + const { + width, + height, + boxSizing, + top, + left, + bounds, + padding, + margin, + } = props; + + const isOutOfBounds = bounds[0] || bounds[1] || bounds[2] || bounds[3]; + + let contentBoxCss = ""; + if (boxSizing === "content-box") { + const diffHeight = padding[0] + padding[2]; + const diffWidth = padding[1] + padding[3]; + contentBoxCss += `height: calc(100% - ${diffHeight}px);`; + contentBoxCss += `width: calc(100% - ${diffWidth}px);`; + } + + const el = this.dom!; + el.style.cssText = `top: ${top}px; left: ${left}px;`; + + const marginDiv = el.firstChild as HTMLDivElement; + marginDiv.style.cssText = `width: ${width}px; height: ${height}px; ${css2Border( + margin, + )}`; + + const borderDiv = marginDiv.firstChild as HTMLDivElement; + borderDiv.style.cssText = css2Border(props.border); + + const contentDiv = borderDiv.firstChild as HTMLDivElement; + contentDiv.style.cssText = `${css2Border(padding)} ${contentBoxCss}`; + + const footer = marginDiv.nextSibling as HTMLSpanElement; + const fixed = isOutOfBounds ? s.fixed : ""; + const fixedLeft = bounds[3] && !bounds[1] ? s.fixedLeft : ""; + const fixedRight = bounds[1] ? s.fixedRight : ""; + const fixedTop = bounds[0] && !bounds[2] ? s.fixedTop : ""; + const fixedBottom = bounds[2] ? s.fixedBottom : ""; + + footer.className = `${s.footer} ${fixed} ${fixedLeft} ${fixedRight} ${fixedTop} ${fixedBottom}`; + + const spanLabel = footer.firstChild! as HTMLSpanElement; + spanLabel.textContent = props.label; + spanLabel.nextElementSibling!.textContent = width + "px"; + spanLabel.nextElementSibling!.nextElementSibling!.textContent = + height + "px"; + } }