mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-06 20:11:38 +08:00
Enable isolation mode for container query containers
screencast: https://imgur.com/a/61Tvewc backend CL: https://crrev.com/c/3173212 Bug: chromium:1146422 Change-Id: I57abf59ae8d22927c3fb514e4dd684d5656b410a Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3175289 Reviewed-by: Alex Rudenko <alexrudenko@chromium.org> Reviewed-by: Johan Bay <jobay@chromium.org> Commit-Queue: Changhao Han <changhaohan@chromium.org>
This commit is contained in:
committed by
Devtools-frontend LUCI CQ
parent
3e7296ac42
commit
8709d85833
@@ -875,6 +875,12 @@ export const SourceOrderHighlight = {
|
||||
ChildOutline: Color.fromRGBA([0, 120, 212, 1]),
|
||||
};
|
||||
|
||||
export const IsolationModeHighlight = {
|
||||
Resizer: Color.fromRGBA([222, 225, 230, 1]), // --color-background-elevation-2
|
||||
ResizerHandle: Color.fromRGBA([166, 166, 166, 1]),
|
||||
Mask: Color.fromRGBA([248, 249, 249, 1]),
|
||||
};
|
||||
|
||||
export class Generator {
|
||||
private readonly hueSpace: number|{
|
||||
min: number,
|
||||
|
||||
@@ -4235,6 +4235,12 @@
|
||||
"panels/elements/ElementsTreeElement.ts | enableScrollSnap": {
|
||||
"message": "Enable scroll-snap overlay"
|
||||
},
|
||||
"panels/elements/ElementsTreeElement.ts | enterIsolationMode": {
|
||||
"message": "Enter Isolation Mode"
|
||||
},
|
||||
"panels/elements/ElementsTreeElement.ts | exitIsolationMode": {
|
||||
"message": "Exit Isolation Mode"
|
||||
},
|
||||
"panels/elements/ElementsTreeElement.ts | expandRecursively": {
|
||||
"message": "Expand recursively"
|
||||
},
|
||||
|
||||
@@ -4235,6 +4235,12 @@
|
||||
"panels/elements/ElementsTreeElement.ts | enableScrollSnap": {
|
||||
"message": "Êńâb́l̂é ŝćr̂ól̂ĺ-ŝńâṕ ôv́êŕl̂áŷ"
|
||||
},
|
||||
"panels/elements/ElementsTreeElement.ts | enterIsolationMode": {
|
||||
"message": "Êńt̂ér̂ Íŝól̂át̂íôń M̂ód̂é"
|
||||
},
|
||||
"panels/elements/ElementsTreeElement.ts | exitIsolationMode": {
|
||||
"message": "Êx́ît́ Îśôĺât́îón̂ Ḿôd́ê"
|
||||
},
|
||||
"panels/elements/ElementsTreeElement.ts | expandRecursively": {
|
||||
"message": "Êx́p̂án̂d́ r̂éĉúr̂śîv́êĺŷ"
|
||||
},
|
||||
|
||||
@@ -467,6 +467,27 @@ export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyA
|
||||
return this.sourceOrderModeActiveInternal;
|
||||
}
|
||||
|
||||
highlightIsolatedElementInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void {
|
||||
if (!this.persistentHighlighter) {
|
||||
return;
|
||||
}
|
||||
this.persistentHighlighter.highlightIsolatedElementInOverlay(nodeId);
|
||||
}
|
||||
|
||||
hideIsolatedElementInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void {
|
||||
if (!this.persistentHighlighter) {
|
||||
return;
|
||||
}
|
||||
this.persistentHighlighter.hideIsolatedElementInOverlay(nodeId);
|
||||
}
|
||||
|
||||
isHighlightedIsolatedElementInPersistentOverlay(nodeId: Protocol.DOM.NodeId): boolean {
|
||||
if (!this.persistentHighlighter) {
|
||||
return false;
|
||||
}
|
||||
return this.persistentHighlighter.isIsolatedElementHighlighted(nodeId);
|
||||
}
|
||||
|
||||
private delayedHideHighlight(delay: number): void {
|
||||
if (this.hideHighlightTimeout === null) {
|
||||
this.hideHighlightTimeout = window.setTimeout(() => this.highlightInOverlay({clear: true}), delay);
|
||||
|
||||
@@ -14,6 +14,7 @@ export class OverlayPersistentHighlighter {
|
||||
private readonly flexHighlights: Map<Protocol.DOM.NodeId, Protocol.Overlay.FlexContainerHighlightConfig>;
|
||||
private readonly containerQueryHighlights:
|
||||
Map<Protocol.DOM.NodeId, Protocol.Overlay.ContainerQueryContainerHighlightConfig>;
|
||||
private readonly isolatedElementHighlights: Map<Protocol.DOM.NodeId, Protocol.Overlay.IsolationModeHighlightConfig>;
|
||||
private readonly colors: Map<Protocol.DOM.NodeId, Common.Color.Color>;
|
||||
private gridColorGenerator: OverlayColorGenerator;
|
||||
private flexColorGenerator: OverlayColorGenerator;
|
||||
@@ -41,6 +42,8 @@ export class OverlayPersistentHighlighter {
|
||||
|
||||
this.containerQueryHighlights = new Map();
|
||||
|
||||
this.isolatedElementHighlights = new Map();
|
||||
|
||||
this.colors = new Map();
|
||||
|
||||
this.gridColorGenerator = new OverlayColorGenerator();
|
||||
@@ -223,11 +226,36 @@ export class OverlayPersistentHighlighter {
|
||||
};
|
||||
}
|
||||
|
||||
highlightIsolatedElementInOverlay(nodeId: Protocol.DOM.NodeId): void {
|
||||
this.isolatedElementHighlights.set(nodeId, this.buildIsolationModeHighlightConfig());
|
||||
this.updateHighlightsInOverlay();
|
||||
}
|
||||
|
||||
hideIsolatedElementInOverlay(nodeId: Protocol.DOM.NodeId): void {
|
||||
if (this.isolatedElementHighlights.has(nodeId)) {
|
||||
this.isolatedElementHighlights.delete(nodeId);
|
||||
this.updateHighlightsInOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
isIsolatedElementHighlighted(nodeId: Protocol.DOM.NodeId): boolean {
|
||||
return this.isolatedElementHighlights.has(nodeId);
|
||||
}
|
||||
|
||||
private buildIsolationModeHighlightConfig(): Protocol.Overlay.IsolationModeHighlightConfig {
|
||||
return {
|
||||
resizerColor: Common.Color.IsolationModeHighlight.Resizer.toProtocolRGBA(),
|
||||
resizerHandleColor: Common.Color.IsolationModeHighlight.ResizerHandle.toProtocolRGBA(),
|
||||
maskColor: Common.Color.IsolationModeHighlight.Mask.toProtocolRGBA(),
|
||||
};
|
||||
}
|
||||
|
||||
hideAllInOverlay(): void {
|
||||
this.flexHighlights.clear();
|
||||
this.gridHighlights.clear();
|
||||
this.scrollSnapHighlights.clear();
|
||||
this.containerQueryHighlights.clear();
|
||||
this.isolatedElementHighlights.clear();
|
||||
this.updateHighlightsInOverlay();
|
||||
}
|
||||
|
||||
@@ -236,7 +264,9 @@ export class OverlayPersistentHighlighter {
|
||||
const flexboxesNeedUpdate = this.updateHighlightsForDeletedNodes(this.flexHighlights);
|
||||
const scrollSnapsNeedUpdate = this.updateHighlightsForDeletedNodes(this.scrollSnapHighlights);
|
||||
const containerQueriesNeedUpdate = this.updateHighlightsForDeletedNodes(this.containerQueryHighlights);
|
||||
if (flexboxesNeedUpdate || gridsNeedUpdate || scrollSnapsNeedUpdate || containerQueriesNeedUpdate) {
|
||||
const isolatedElementsNeedUpdate = this.updateHighlightsForDeletedNodes(this.isolatedElementHighlights);
|
||||
if (flexboxesNeedUpdate || gridsNeedUpdate || scrollSnapsNeedUpdate || containerQueriesNeedUpdate ||
|
||||
isolatedElementsNeedUpdate) {
|
||||
this.updateHighlightsInOverlay();
|
||||
}
|
||||
}
|
||||
@@ -265,17 +295,21 @@ export class OverlayPersistentHighlighter {
|
||||
for (const nodeId of this.containerQueryHighlights.keys()) {
|
||||
this.containerQueryHighlights.set(nodeId, this.buildContainerQueryContainerHighlightConfig());
|
||||
}
|
||||
for (const nodeId of this.isolatedElementHighlights.keys()) {
|
||||
this.isolatedElementHighlights.set(nodeId, this.buildIsolationModeHighlightConfig());
|
||||
}
|
||||
this.updateHighlightsInOverlay();
|
||||
}
|
||||
|
||||
private updateHighlightsInOverlay(): void {
|
||||
const hasNodesToHighlight =
|
||||
this.gridHighlights.size > 0 || this.flexHighlights.size > 0 || this.containerQueryHighlights.size > 0;
|
||||
const hasNodesToHighlight = this.gridHighlights.size > 0 || this.flexHighlights.size > 0 ||
|
||||
this.containerQueryHighlights.size > 0 || this.isolatedElementHighlights.size > 0;
|
||||
this.model.setShowViewportSizeOnResize(!hasNodesToHighlight);
|
||||
this.updateGridHighlightsInOverlay();
|
||||
this.updateFlexHighlightsInOverlay();
|
||||
this.updateScrollSnapHighlightsInOverlay();
|
||||
this.updateContainerQueryHighlightsInOverlay();
|
||||
this.updateIsolatedElementHighlightsInOverlay();
|
||||
}
|
||||
|
||||
private updateGridHighlightsInOverlay(): void {
|
||||
@@ -316,6 +350,15 @@ export class OverlayPersistentHighlighter {
|
||||
}
|
||||
overlayModel.target().overlayAgent().invoke_setShowContainerQueryOverlays({containerQueryHighlightConfigs});
|
||||
}
|
||||
|
||||
updateIsolatedElementHighlightsInOverlay(): void {
|
||||
const overlayModel = this.model;
|
||||
const isolatedElementHighlightConfigs = [];
|
||||
for (const [nodeId, isolationModeHighlightConfig] of this.isolatedElementHighlights.entries()) {
|
||||
isolatedElementHighlightConfigs.push({nodeId, isolationModeHighlightConfig});
|
||||
}
|
||||
overlayModel.target().overlayAgent().invoke_setShowIsolatedElements({isolatedElementHighlightConfigs});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,6 +405,15 @@ export interface OverlayAgent {
|
||||
containerQueryContainerHighlightConfig: Protocol.Overlay.ContainerQueryContainerHighlightConfig,
|
||||
}>,
|
||||
}): void;
|
||||
|
||||
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
invoke_setShowIsolatedElements(param: {
|
||||
isolatedElementHighlightConfigs: Array<{
|
||||
nodeId: number,
|
||||
isolationModeHighlightConfig: Protocol.Overlay.IsolationModeHighlightConfig,
|
||||
}>,
|
||||
}): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,6 +83,14 @@ const UIStrings = {
|
||||
*/
|
||||
scrollIntoView: 'Scroll into view',
|
||||
/**
|
||||
*@description Text to enter Isolation Mode, a mode with focus on a single element and interactive resizing
|
||||
*/
|
||||
enterIsolationMode: 'Enter Isolation Mode',
|
||||
/**
|
||||
*@description Text to exit Isolation Mode, a mode with focus on a single element and interactive resizing
|
||||
*/
|
||||
exitIsolationMode: 'Exit Isolation Mode',
|
||||
/**
|
||||
*@description A context menu item in the Elements Tree Element of the Elements panel
|
||||
*/
|
||||
editText: 'Edit text',
|
||||
@@ -636,6 +644,17 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
||||
contextMenu.viewSection().appendItem(i18nString(UIStrings.focus), async () => {
|
||||
await this.nodeInternal.focus();
|
||||
});
|
||||
|
||||
const overlayModel = this.nodeInternal.domModel().overlayModel();
|
||||
if (overlayModel.isHighlightedIsolatedElementInPersistentOverlay(this.nodeInternal.id)) {
|
||||
contextMenu.viewSection().appendItem(i18nString(UIStrings.exitIsolationMode), () => {
|
||||
overlayModel.hideIsolatedElementInPersistentOverlay(this.nodeInternal.id);
|
||||
});
|
||||
} else {
|
||||
contextMenu.viewSection().appendItem(i18nString(UIStrings.enterIsolationMode), () => {
|
||||
overlayModel.highlightIsolatedElementInPersistentOverlay(this.nodeInternal.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
populateScrollIntoView(contextMenu: UI.ContextMenu.ContextMenu): void {
|
||||
|
||||
@@ -14,10 +14,12 @@ action("build_inspector_overlay") {
|
||||
"$target_gen_dir/common.css",
|
||||
"$target_gen_dir/common.js",
|
||||
"$target_gen_dir/css_grid_label_helpers.js",
|
||||
"$target_gen_dir/drag_resize_handler.js",
|
||||
"$target_gen_dir/highlight_common.js",
|
||||
"$target_gen_dir/highlight_container_query.js",
|
||||
"$target_gen_dir/highlight_flex_common.js",
|
||||
"$target_gen_dir/highlight_grid_common.js",
|
||||
"$target_gen_dir/highlight_isolated_element.js",
|
||||
"$target_gen_dir/highlight_scroll_snap.js",
|
||||
"$target_gen_dir/tool_distances.js",
|
||||
"$target_gen_dir/tool_persistent.js",
|
||||
@@ -40,7 +42,7 @@ action("build_inspector_overlay") {
|
||||
|
||||
args = [
|
||||
"main.js",
|
||||
"65000", # max_size in bytes
|
||||
"70000", # max_size in bytes
|
||||
"--input_path",
|
||||
rebase_path(target_gen_dir, root_build_dir),
|
||||
"--output_path",
|
||||
@@ -77,10 +79,12 @@ ts_library("inspector_overlay") {
|
||||
sources = [
|
||||
"common.ts",
|
||||
"css_grid_label_helpers.ts",
|
||||
"drag_resize_handler.ts",
|
||||
"highlight_common.ts",
|
||||
"highlight_container_query.ts",
|
||||
"highlight_flex_common.ts",
|
||||
"highlight_grid_common.ts",
|
||||
"highlight_isolated_element.ts",
|
||||
"highlight_scroll_snap.ts",
|
||||
"main.ts",
|
||||
"tool_distances.ts",
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<!--
|
||||
Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be
|
||||
found in the LICENSE file.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Debug</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<script type="module" src="/out/Default/resources/inspector_overlay/main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
const viewportWidth = document.documentElement.clientWidth;
|
||||
const viewportHeight = document.documentElement.clientHeight;
|
||||
dispatch(["setPlatform", "other"]);
|
||||
dispatch(["setOverlay", "persistent"]);
|
||||
dispatch(["reset", {
|
||||
viewportSize: {
|
||||
width: viewportWidth,
|
||||
height: viewportHeight,
|
||||
},
|
||||
deviceScaleFactor: 1,
|
||||
pageScaleFactor: 1,
|
||||
pageZoomFactor: 1,
|
||||
emulationScaleFactor: 1,
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
}]);
|
||||
dispatch(["drawIsolatedElementHighlight", {
|
||||
"draggableWidthBorder": [
|
||||
"M",
|
||||
300,
|
||||
100,
|
||||
"L",
|
||||
350,
|
||||
100,
|
||||
"L",
|
||||
350,
|
||||
300,
|
||||
"L",
|
||||
300,
|
||||
300,
|
||||
"Z"
|
||||
],
|
||||
"draggableHeightBorder": [
|
||||
"M",
|
||||
100,
|
||||
300,
|
||||
"L",
|
||||
300,
|
||||
300,
|
||||
"L",
|
||||
300,
|
||||
350,
|
||||
"L",
|
||||
100,
|
||||
350,
|
||||
"Z"
|
||||
],
|
||||
"currentWidth": 200,
|
||||
"currentHeight": 200,
|
||||
"currentX": 100,
|
||||
"currentY": 100,
|
||||
"highlightIndex": 1,
|
||||
}]);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
export const enum ResizerType {
|
||||
WIDTH = 'width',
|
||||
HEIGHT = 'height',
|
||||
BIDIRECTION = 'bidirection',
|
||||
}
|
||||
|
||||
export interface Draggable {
|
||||
type: ResizerType;
|
||||
initialWidth?: number;
|
||||
initialHeight?: number;
|
||||
update({width, height}: {width?: number, height?: number}): void;
|
||||
}
|
||||
|
||||
export interface Delegate {
|
||||
getDraggable(x: number, y: number): Draggable|undefined;
|
||||
}
|
||||
|
||||
const cursorByResizerType = new Map([
|
||||
[ResizerType.WIDTH, 'ew-resize'],
|
||||
[ResizerType.HEIGHT, 'ns-resize'],
|
||||
[ResizerType.BIDIRECTION, 'nwse-resize'],
|
||||
]);
|
||||
|
||||
type OriginInfo = {
|
||||
coord: number,
|
||||
value: number,
|
||||
};
|
||||
|
||||
export class DragResizeHandler {
|
||||
private document: Document;
|
||||
private delegate: Delegate;
|
||||
private originX?: OriginInfo;
|
||||
private originY?: OriginInfo;
|
||||
private boundMousemove: (event: MouseEvent) => void;
|
||||
private boundMousedown: (event: MouseEvent) => void;
|
||||
|
||||
constructor(document: Document, delegate: Delegate) {
|
||||
this.document = document;
|
||||
this.delegate = delegate;
|
||||
this.boundMousemove = this.onMousemove.bind(this);
|
||||
this.boundMousedown = this.onMousedown.bind(this);
|
||||
}
|
||||
|
||||
install() {
|
||||
this.document.body.addEventListener('mousemove', this.boundMousemove);
|
||||
this.document.body.addEventListener('mousedown', this.boundMousedown);
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
this.document.body.removeEventListener('mousemove', this.boundMousemove);
|
||||
this.document.body.removeEventListener('mousedown', this.boundMousedown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cursor style of the mouse is hovered over a resizeable area.
|
||||
*/
|
||||
private onMousemove(event: MouseEvent) {
|
||||
const match = this.delegate.getDraggable(event.clientX, event.clientY);
|
||||
if (!match) {
|
||||
this.document.body.style.cursor = 'default';
|
||||
return;
|
||||
}
|
||||
this.document.body.style.cursor = cursorByResizerType.get(match.type) || 'default';
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts dragging
|
||||
*/
|
||||
private onMousedown(event: MouseEvent) {
|
||||
const match = this.delegate.getDraggable(event.clientX, event.clientY);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
const boundOnDrag = this.onDrag.bind(this, match);
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (match.initialWidth !== undefined &&
|
||||
(match.type === ResizerType.WIDTH || match.type === ResizerType.BIDIRECTION)) {
|
||||
this.originX = {
|
||||
coord: Math.round(event.clientX),
|
||||
value: match.initialWidth,
|
||||
};
|
||||
}
|
||||
|
||||
if (match.initialHeight !== undefined &&
|
||||
(match.type === ResizerType.HEIGHT || match.type === ResizerType.BIDIRECTION)) {
|
||||
this.originY = {
|
||||
coord: Math.round(event.clientY),
|
||||
value: match.initialHeight,
|
||||
};
|
||||
}
|
||||
|
||||
this.document.body.removeEventListener('mousemove', this.boundMousemove);
|
||||
this.document.body.style.cursor = cursorByResizerType.get(match.type) || 'default';
|
||||
|
||||
const endDrag = (event: Event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.originX = undefined;
|
||||
this.originY = undefined;
|
||||
|
||||
this.document.body.style.cursor = 'default';
|
||||
this.document.body.removeEventListener('mousemove', boundOnDrag);
|
||||
this.document.body.addEventListener('mousemove', this.boundMousemove);
|
||||
};
|
||||
|
||||
this.document.body.addEventListener('mouseup', endDrag, {once: true});
|
||||
window.addEventListener('mouseout', endDrag, {once: true});
|
||||
|
||||
this.document.body.addEventListener('mousemove', boundOnDrag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the new value while the cursor is being dragged and calls InspectorOverlayHost with the new value.
|
||||
*/
|
||||
private onDrag(match: Draggable, e: MouseEvent) {
|
||||
if (!this.originX && !this.originY) {
|
||||
return;
|
||||
}
|
||||
|
||||
let width: number|undefined;
|
||||
let height: number|undefined;
|
||||
if (this.originX) {
|
||||
const delta = this.originX.coord - e.clientX;
|
||||
width = Math.round(this.originX.value - delta);
|
||||
}
|
||||
|
||||
if (this.originY) {
|
||||
const delta = this.originY.coord - e.clientY;
|
||||
height = Math.round(this.originY.value - delta);
|
||||
}
|
||||
|
||||
match.update({width, height});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import type {PathCommands} from './common.js';
|
||||
import {buildPath, emptyBounds, fillPathWithBoxStyle} from './highlight_common.js';
|
||||
|
||||
export interface IsolatedElementHighlight {
|
||||
widthResizerBorder: PathCommands;
|
||||
heightResizerBorder: PathCommands;
|
||||
bidirectionResizerBorder: PathCommands;
|
||||
currentX: number;
|
||||
currentY: number;
|
||||
currentWidth: number;
|
||||
currentHeight: number;
|
||||
highlightIndex: number;
|
||||
isolationModeHighlightConfig: {
|
||||
resizerColor: string,
|
||||
resizerHandleColor: string,
|
||||
maskColor: string,
|
||||
};
|
||||
}
|
||||
|
||||
export function drawIsolatedElementHighlight(
|
||||
highlight: IsolatedElementHighlight, context: CanvasRenderingContext2D, canvasWidth: number, canvasHeight: number,
|
||||
emulationScaleFactor: number) {
|
||||
const {currentX, currentY, currentWidth, currentHeight, highlightIndex} = highlight;
|
||||
// Draw a mask covering other area of the canvas.
|
||||
context.save();
|
||||
context.fillStyle = highlight.isolationModeHighlightConfig.maskColor;
|
||||
context.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||
context.clearRect(currentX, currentY, currentWidth, currentHeight);
|
||||
context.restore();
|
||||
|
||||
// Draw the width resizer with handle bars.
|
||||
const bounds = emptyBounds();
|
||||
const widthPath = buildPath(highlight.widthResizerBorder, bounds, emulationScaleFactor);
|
||||
fillPathWithBoxStyle(context, widthPath, bounds, 0 /* angle */, {
|
||||
fillColor: highlight.isolationModeHighlightConfig.resizerColor,
|
||||
});
|
||||
|
||||
// Draw the height resizer with handle bars.
|
||||
const heightPath = buildPath(highlight.heightResizerBorder, bounds, emulationScaleFactor);
|
||||
fillPathWithBoxStyle(context, heightPath, bounds, 0 /* angle */, {
|
||||
fillColor: highlight.isolationModeHighlightConfig.resizerColor,
|
||||
});
|
||||
|
||||
// Draw the bidirection resizer with handle bars.
|
||||
const bidirectionPath = buildPath(highlight.bidirectionResizerBorder, bounds, emulationScaleFactor);
|
||||
fillPathWithBoxStyle(context, bidirectionPath, bounds, 0 /* angle */, {
|
||||
fillColor: highlight.isolationModeHighlightConfig.resizerColor,
|
||||
});
|
||||
|
||||
return {
|
||||
widthPath,
|
||||
heightPath,
|
||||
bidirectionPath,
|
||||
currentWidth,
|
||||
currentHeight,
|
||||
highlightIndex,
|
||||
};
|
||||
}
|
||||
@@ -14,7 +14,9 @@ import highlightStyle from './tool_highlight.css'; // eslint-disable-line rulesd
|
||||
import {HighlightOverlay} from './tool_highlight.js';
|
||||
// @ts-ignore Importing CSS is handled in Rollup.
|
||||
import pausedStyle from './tool_paused.css'; // eslint-disable-line rulesdir/es_modules_import
|
||||
import type {PausedToolMessage} from './tool_paused.js';
|
||||
import {PausedOverlay} from './tool_paused.js';
|
||||
import type {PersistentToolMessage} from './tool_persistent.js';
|
||||
import {PersistentOverlay} from './tool_persistent.js';
|
||||
// @ts-ignore Importing CSS is handled in Rollup.
|
||||
import screenshotStyle from './tool_screenshot.css'; // eslint-disable-line rulesdir/es_modules_import
|
||||
@@ -28,7 +30,7 @@ declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Window {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
InspectorOverlayHost: {send(data: string): void};
|
||||
InspectorOverlayHost: {send(data: PausedToolMessage|PersistentToolMessage|string): void};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import {drawLayoutGridHighlight} from './highlight_grid_common.js';
|
||||
import type {ScrollSnapHighlight} from './highlight_scroll_snap.js';
|
||||
import type {ContainerQueryHighlight} from './highlight_container_query.js';
|
||||
import {drawContainerQueryHighlight} from './highlight_container_query.js';
|
||||
import type {IsolatedElementHighlight} from './highlight_isolated_element.js';
|
||||
import {PersistentOverlay} from './tool_persistent.js';
|
||||
|
||||
interface Path {
|
||||
@@ -85,6 +86,7 @@ interface Highlight {
|
||||
flexInfo: FlexContainerHighlight[];
|
||||
flexItemInfo: FlexItemHighlight[];
|
||||
containerQueryInfo: ContainerQueryHighlight[];
|
||||
isolatedElementInfo: IsolatedElementHighlight[];
|
||||
}
|
||||
|
||||
export class HighlightOverlay extends Overlay {
|
||||
@@ -245,6 +247,10 @@ export class HighlightOverlay extends Overlay {
|
||||
this.persistentOverlay?.drawContainerQueryHighlight(highlight);
|
||||
}
|
||||
|
||||
drawIsolatedElementHighlight(highlight: IsolatedElementHighlight) {
|
||||
this.persistentOverlay?.drawIsolatedElementHighlight(highlight);
|
||||
}
|
||||
|
||||
private drawAxis(context: CanvasRenderingContext2D, rulerAtRight: boolean, rulerAtBottom: boolean) {
|
||||
context.save();
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
import {Overlay} from './common.js';
|
||||
|
||||
export type PausedToolMessage = 'resume'|'stepOver';
|
||||
|
||||
export class PausedOverlay extends Overlay {
|
||||
private container!: HTMLElement;
|
||||
|
||||
|
||||
@@ -30,19 +30,74 @@
|
||||
|
||||
import type {ResetData} from './common.js';
|
||||
import {Overlay} from './common.js';
|
||||
import type {Delegate} from './drag_resize_handler.js';
|
||||
import {DragResizeHandler, ResizerType} from './drag_resize_handler.js';
|
||||
import type {ContainerQueryHighlight} from './highlight_container_query.js';
|
||||
import {drawContainerQueryHighlight} from './highlight_container_query.js';
|
||||
import type {FlexContainerHighlight} from './highlight_flex_common.js';
|
||||
import {drawLayoutFlexContainerHighlight} from './highlight_flex_common.js';
|
||||
import type {GridHighlight} from './highlight_grid_common.js';
|
||||
import {drawLayoutGridHighlight} from './highlight_grid_common.js';
|
||||
import type {IsolatedElementHighlight} from './highlight_isolated_element.js';
|
||||
import {drawIsolatedElementHighlight} from './highlight_isolated_element.js';
|
||||
import type {ScrollSnapHighlight} from './highlight_scroll_snap.js';
|
||||
import {drawScrollSnapHighlight} from './highlight_scroll_snap.js';
|
||||
|
||||
export type PersistentToolMessage = {
|
||||
highlightType: string,
|
||||
highlightIndex: number,
|
||||
newWidth: string,
|
||||
newHeight: string,
|
||||
resizerType: ResizerType,
|
||||
};
|
||||
|
||||
interface DraggableMetadata {
|
||||
type: ResizerType;
|
||||
highlightIndex: number;
|
||||
initialWidth?: number;
|
||||
initialHeight?: number;
|
||||
}
|
||||
|
||||
function makeDraggableDelegate(overlay: PersistentOverlay): Delegate {
|
||||
return {
|
||||
getDraggable: (x, y) => {
|
||||
const result = overlay.isPointInDraggablePath(x, y);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
type: result.type,
|
||||
initialWidth: result.initialWidth,
|
||||
initialHeight: result.initialHeight,
|
||||
id: result.highlightIndex,
|
||||
update: ({width, height}: {width?: number, height?: number}) => {
|
||||
window.InspectorOverlayHost.send({
|
||||
highlightType: 'isolatedElement',
|
||||
highlightIndex: result.highlightIndex,
|
||||
newWidth: `${width}px`,
|
||||
newHeight: `${height}px`,
|
||||
resizerType: result.type,
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export class PersistentOverlay extends Overlay {
|
||||
private gridLabelState = {gridLayerCounter: 0};
|
||||
|
||||
private gridLabels!: HTMLElement;
|
||||
private draggableBorders: Map<number, {
|
||||
widthPath: Path2D,
|
||||
heightPath: Path2D,
|
||||
bidirectionPath: Path2D,
|
||||
highlightIndex: number,
|
||||
initialWidth: number,
|
||||
initialHeight: number,
|
||||
}> = new Map();
|
||||
private dragHandler?: DragResizeHandler;
|
||||
|
||||
reset(data: ResetData) {
|
||||
super.reset(data);
|
||||
@@ -70,12 +125,15 @@ export class PersistentOverlay extends Overlay {
|
||||
this.setCanvas(canvas);
|
||||
|
||||
super.install();
|
||||
this.dragHandler?.install();
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
this.document.body.classList.remove('fill');
|
||||
this.document.body.innerHTML = '';
|
||||
this.draggableBorders = new Map();
|
||||
super.uninstall();
|
||||
this.dragHandler?.uninstall();
|
||||
}
|
||||
|
||||
drawGridHighlight(highlight: GridHighlight) {
|
||||
@@ -105,4 +163,56 @@ export class PersistentOverlay extends Overlay {
|
||||
drawContainerQueryHighlight(highlight, this.context, this.emulationScaleFactor);
|
||||
this.context.restore();
|
||||
}
|
||||
|
||||
drawIsolatedElementHighlight(highlight: IsolatedElementHighlight) {
|
||||
if (!this.dragHandler) {
|
||||
this.dragHandler = new DragResizeHandler(this.document, makeDraggableDelegate(this));
|
||||
this.dragHandler.install();
|
||||
}
|
||||
|
||||
this.context.save();
|
||||
const {widthPath, heightPath, bidirectionPath, currentWidth, currentHeight, highlightIndex} =
|
||||
drawIsolatedElementHighlight(
|
||||
highlight, this.context, this.canvasWidth, this.canvasHeight, this.emulationScaleFactor);
|
||||
|
||||
this.draggableBorders.set(highlightIndex, {
|
||||
widthPath,
|
||||
heightPath,
|
||||
bidirectionPath,
|
||||
highlightIndex,
|
||||
initialWidth: currentWidth,
|
||||
initialHeight: currentHeight,
|
||||
});
|
||||
this.context.restore();
|
||||
}
|
||||
|
||||
isPointInDraggablePath(x: number, y: number): DraggableMetadata|undefined {
|
||||
for (const {widthPath, heightPath, bidirectionPath, highlightIndex, initialWidth, initialHeight} of this
|
||||
.draggableBorders.values()) {
|
||||
if (this.context.isPointInPath(widthPath, x, y)) {
|
||||
return {
|
||||
type: ResizerType.WIDTH,
|
||||
highlightIndex,
|
||||
initialWidth,
|
||||
};
|
||||
}
|
||||
if (this.context.isPointInPath(heightPath, x, y)) {
|
||||
return {
|
||||
type: ResizerType.HEIGHT,
|
||||
highlightIndex,
|
||||
initialHeight,
|
||||
};
|
||||
}
|
||||
if (this.context.isPointInPath(bidirectionPath, x, y)) {
|
||||
return {
|
||||
type: ResizerType.BIDIRECTION,
|
||||
highlightIndex,
|
||||
initialWidth,
|
||||
initialHeight,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ node_ts_library("elements") {
|
||||
"element-breadcrumbs_test.ts",
|
||||
"flexbox-editor_test.ts",
|
||||
"grid-editor_test.ts",
|
||||
"isolation-mode_test.ts",
|
||||
"layout-pane_test.ts",
|
||||
"pseudo-states_test.ts",
|
||||
"reveal-correct-node_test.ts",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import {goToResource} from '../../shared/helper.js';
|
||||
import {describe, it} from '../../shared/mocha-extensions.js';
|
||||
import {clickOnContextMenu} from '../helpers/sources-helpers.js';
|
||||
|
||||
describe('The Elements panel', async () => {
|
||||
it('has a context menu item to enter Isolation Mode', async () => {
|
||||
await goToResource('elements/css-container-queries.html');
|
||||
await clickOnContextMenu('[aria-label="</body>"]', 'Enter Isolation Mode');
|
||||
await clickOnContextMenu('[aria-label="</body>"]', 'Exit Isolation Mode');
|
||||
});
|
||||
});
|
||||
@@ -31,6 +31,7 @@ describeWithEnvironment('OverlayColorGenerator', () => {
|
||||
invoke_setShowFlexOverlays() {},
|
||||
invoke_setShowScrollSnapOverlays() {},
|
||||
invoke_setShowContainerQueryOverlays() {},
|
||||
invoke_setShowIsolatedElements() {},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -80,4 +81,16 @@ describeWithEnvironment('OverlayColorGenerator', () => {
|
||||
highlighter.hideContainerQueryInOverlay(nodeId);
|
||||
assert(!highlighter.isContainerQueryHighlighted(nodeId));
|
||||
});
|
||||
|
||||
it('is able to highlight isolated elements', () => {
|
||||
const highlighter = new SDK.OverlayPersistentHighlighter.OverlayPersistentHighlighter(mockModel);
|
||||
highlighter.highlightIsolatedElementInOverlay(nodeId);
|
||||
assert(highlighter.isIsolatedElementHighlighted(nodeId));
|
||||
assert(!highlighter.isFlexHighlighted(nodeId));
|
||||
assert(!highlighter.isGridHighlighted(nodeId));
|
||||
assert(!highlighter.isScrollSnapHighlighted(nodeId));
|
||||
assert(!highlighter.isContainerQueryHighlighted(nodeId));
|
||||
highlighter.hideIsolatedElementInOverlay(nodeId);
|
||||
assert(!highlighter.isIsolatedElementHighlighted(nodeId));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user