mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] devtools: linting and context menu rework
This commit cleans up linting issues and proposes a new implementation for the devtools custom context menus.
This commit is contained in:
committed by
Géry Debongnie
parent
5ecb4809ff
commit
aff4019cde
@@ -0,0 +1,36 @@
|
|||||||
|
import { useStore } from "../store/store";
|
||||||
|
|
||||||
|
const { Component, useEffect, useRef } = owl;
|
||||||
|
|
||||||
|
export class ContextMenu extends Component {
|
||||||
|
static template = "devtools.ContextMenu";
|
||||||
|
static props = {
|
||||||
|
items: Array,
|
||||||
|
};
|
||||||
|
setup() {
|
||||||
|
this.store = useStore();
|
||||||
|
this.contextMenu = useRef("contextmenu");
|
||||||
|
useEffect(
|
||||||
|
(position) => {
|
||||||
|
const menu = this.contextMenu.el;
|
||||||
|
const menuWidth = menu.offsetWidth;
|
||||||
|
const menuHeight = menu.offsetHeight;
|
||||||
|
let { x, y } = position;
|
||||||
|
if (x + menuWidth > window.innerWidth) {
|
||||||
|
x = window.innerWidth - menuWidth;
|
||||||
|
}
|
||||||
|
if (y + menuHeight > window.innerHeight) {
|
||||||
|
y = window.innerHeight - menuHeight;
|
||||||
|
}
|
||||||
|
menu.style.left = x + "px";
|
||||||
|
// Need 25px offset because of the main navbar from the browser devtools
|
||||||
|
menu.style.top = y + "px";
|
||||||
|
},
|
||||||
|
() => [this.store.contextMenu?.position]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onClickItem(action) {
|
||||||
|
action();
|
||||||
|
this.store.contextMenu = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="devtools.ContextMenu">
|
||||||
|
<div class="custom-menu" t-ref="contextmenu">
|
||||||
|
<ul class="my-1">
|
||||||
|
<li t-foreach="props.items" t-as="item" t-key="item_index" t-if="item.show" t-esc="item.title" t-on-click.stop="() => this.onClickItem(item.action)" class="custom-menu-item py-1 px-4"/>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
+55
-14
@@ -1,4 +1,4 @@
|
|||||||
const { Component, useRef, useEffect } = owl;
|
const { Component } = owl;
|
||||||
import { useStore } from "../../../store/store";
|
import { useStore } from "../../../store/store";
|
||||||
import { ObjectTreeElement } from "./object_tree_element/object_tree_element";
|
import { ObjectTreeElement } from "./object_tree_element/object_tree_element";
|
||||||
|
|
||||||
@@ -7,23 +7,64 @@ export class DetailsWindow extends Component {
|
|||||||
static components = { ObjectTreeElement };
|
static components = { ObjectTreeElement };
|
||||||
setup() {
|
setup() {
|
||||||
this.store = useStore();
|
this.store = useStore();
|
||||||
this.contextMenu = useRef("contextmenu");
|
}
|
||||||
this.contextMenuId = this.store.contextMenu.id++;
|
|
||||||
this.contextMenuEvent;
|
get contextMenuItems() {
|
||||||
// Open the context menu when the ids match
|
return [
|
||||||
useEffect(
|
{
|
||||||
(menuId) => {
|
title: "Inspect source code",
|
||||||
if (menuId === this.contextMenuId) {
|
show: true,
|
||||||
this.store.contextMenu.open(this.contextMenuEvent, this.contextMenu.el);
|
action: () => this.store.inspectComponent("source", this.store.activeComponent.path),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
() => [this.store.contextMenu.activeMenu]
|
{
|
||||||
);
|
title: "Store as global variable",
|
||||||
|
show: this.store.activeComponent.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.store.activeComponent.path,
|
||||||
|
{ type: "item", value: "component" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect in Elements tab",
|
||||||
|
show: this.store.activeComponent.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("DOM", this.store.activeComponent.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Force rerender",
|
||||||
|
show: this.store.activeComponent.path.length !== 1,
|
||||||
|
action: () => this.store.refreshComponent(this.store.activeComponent.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store observed states as global variable",
|
||||||
|
show: this.store.activeComponent.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.store.activeComponent.path,
|
||||||
|
{ type: "item", value: "subscriptions" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect compiled template",
|
||||||
|
show: this.store.activeComponent.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.inspectComponent("compiled template", this.store.activeComponent.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Log raw template",
|
||||||
|
show: this.store.activeComponent.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("raw template", this.store.activeComponent.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: this.store.activeComponent.path.length === 1,
|
||||||
|
action: () => this.store.logObjectInConsole([...this.store.activeComponent.path]),
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
openMenu(ev) {
|
openMenu(ev) {
|
||||||
this.contextMenuEvent = ev;
|
this.store.openContextMenu(ev, this.contextMenuItems);
|
||||||
this.store.contextMenu.activeMenu = this.contextMenuId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCategory(ev, category) {
|
toggleCategory(ev, category) {
|
||||||
|
|||||||
+1
-17
@@ -76,21 +76,5 @@
|
|||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="store.contextMenu.activeMenu === contextMenuId" class="custom-menu" t-ref="contextmenu">
|
|
||||||
<ul class="my-1">
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('source', store.activeComponent.path)" class="custom-menu-item py-1 px-4">Inspect source code</li>
|
|
||||||
<t t-if="store.activeComponent.path.length !== 1">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...store.activeComponent.path, { type: 'item', value: 'component'}])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('DOM', store.activeComponent.path)" class="custom-menu-item py-1 px-4">Inspect in Elements tab</li>
|
|
||||||
<li t-on-click.stop="() => this.store.refreshComponent(store.activeComponent.path)" class="custom-menu-item py-1 px-4">Force rerender</li>
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...store.activeComponent.path, { type: 'item', value: 'subscriptions'}])" class="custom-menu-item py-1 px-4">Store observed states as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('compiled template', store.activeComponent.path)" class="custom-menu-item py-1 px-4">Inspect compiled template</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('raw template', store.activeComponent.path)" class="custom-menu-item py-1 px-4">Log raw template</li>
|
|
||||||
</t>
|
|
||||||
<t t-else="">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...store.activeComponent.path])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
</t>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</t>
|
</t>
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
+16
-13
@@ -13,19 +13,8 @@ export class ObjectTreeElement extends Component {
|
|||||||
menuTop: 0,
|
menuTop: 0,
|
||||||
menuLeft: 0,
|
menuLeft: 0,
|
||||||
});
|
});
|
||||||
this.contextMenu = useRef("contextmenu");
|
|
||||||
const inputRef = useRef("input");
|
const inputRef = useRef("input");
|
||||||
this.store = useStore();
|
this.store = useStore();
|
||||||
this.contextMenuId = this.store.contextMenu.id++;
|
|
||||||
this.contextMenuEvent,
|
|
||||||
useEffect(
|
|
||||||
(menuId) => {
|
|
||||||
if (menuId === this.contextMenuId) {
|
|
||||||
this.store.contextMenu.open(this.contextMenuEvent, this.contextMenu.el);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => [this.store.contextMenu.activeMenu]
|
|
||||||
);
|
|
||||||
useEffect(
|
useEffect(
|
||||||
(editMode) => {
|
(editMode) => {
|
||||||
// Focus on the input when it is created
|
// Focus on the input when it is created
|
||||||
@@ -63,9 +52,23 @@ export class ObjectTreeElement extends Component {
|
|||||||
return this.props.object.depth * 0.8 + 0.3;
|
return this.props.object.depth * 0.8 + 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get contextMenuItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.logObjectInConsole(this.props.object.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect function source code",
|
||||||
|
show: this.props.object.contentType === "function",
|
||||||
|
action: () => this.store.inspectFunctionSource(this.props.object.path),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
openMenu(ev) {
|
openMenu(ev) {
|
||||||
this.contextMenuEvent = ev;
|
this.store.openContextMenu(ev, this.contextMenuItems);
|
||||||
this.store.contextMenu.activeMenu = this.contextMenuId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEditMode() {
|
setupEditMode() {
|
||||||
|
|||||||
+4
-12
@@ -4,7 +4,7 @@
|
|||||||
<div class="m-0 p-0 text-nowrap w-100 object-line"
|
<div class="m-0 p-0 text-nowrap w-100 object-line"
|
||||||
t-att-class="props.class + (props.object.hasChildren ? ' bg-feedback' : '')"
|
t-att-class="props.class + (props.object.hasChildren ? ' bg-feedback' : '')"
|
||||||
t-on-click.stop="() => this.store.toggleObjectTreeElementsDisplay(this.props.object)"
|
t-on-click.stop="() => this.store.toggleObjectTreeElementsDisplay(this.props.object)"
|
||||||
t-on-contextmenu.prevent="openMenu"
|
t-on-contextmenu.prevent="openMenu"
|
||||||
>
|
>
|
||||||
<div t-attf-style="padding-left: {{objectPadding}}rem">
|
<div t-attf-style="padding-left: {{objectPadding}}rem">
|
||||||
<i class="fa px-1 pointer-icon caret"
|
<i class="fa px-1 pointer-icon caret"
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
t-attf-style="visibility: {{props.object.hasChildren ? '' : 'hidden'}};"
|
t-attf-style="visibility: {{props.object.hasChildren ? '' : 'hidden'}};"
|
||||||
/>
|
/>
|
||||||
<t t-esc="props.object.name"/>
|
<t t-esc="props.object.name"/>
|
||||||
<t t-if="props.object.content.length > 0">: </t>
|
<t t-if="props.object.content.length > 0">: </t>
|
||||||
<t t-if="props.object.contentType == 'getter'">
|
<t t-if="props.object.contentType == 'getter'">
|
||||||
<span class="getter-content object-content" t-att-class="objectLineClass" t-on-click.stop="() => this.store.loadGetterContent(this.props.object)">
|
<span class="getter-content object-content" t-att-class="objectLineClass" t-on-click.stop="() => this.store.loadGetterContent(this.props.object)">
|
||||||
<t t-esc="props.object.content"/>
|
<t t-esc="props.object.content"/>
|
||||||
@@ -20,25 +20,17 @@
|
|||||||
</t>
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<span class="object-content" t-att-class="objectLineClass" t-on-dblclick.stop="setupEditMode">
|
<span class="object-content" t-att-class="objectLineClass" t-on-dblclick.stop="setupEditMode">
|
||||||
<t t-if="state.editMode">
|
<t t-if="state.editMode">
|
||||||
<input t-attf-id="objectEditionInput/{{pathAsString}}" t-ref="input" type="text" placeholder="" t-att-value="props.object.content" t-on-keydown.stop="editObject"/>
|
<input t-attf-id="objectEditionInput/{{pathAsString}}" t-ref="input" type="text" placeholder="" t-att-value="props.object.content" t-on-keydown.stop="editObject"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<t t-esc="props.object.content"/>
|
<t t-esc="props.object.content"/>
|
||||||
</t>
|
</t>
|
||||||
</span>
|
</span>
|
||||||
</t>
|
</t>
|
||||||
<span t-if="keyChanges" class="key-changes ms-1 badge p-1" title="Key additions/deletions are observed">+/-</span>
|
<span t-if="keyChanges" class="key-changes ms-1 badge p-1" title="Key additions/deletions are observed">+/-</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="store.contextMenu.activeMenu === contextMenuId" class="custom-menu" t-ref="contextmenu">
|
|
||||||
<ul class="my-1">
|
|
||||||
<li t-on-click="() => this.store.logObjectInConsole(this.props.object.path)" class="custom-menu-item py-1 px-4 text-nowrap">Store as global variable</li>
|
|
||||||
<t t-if='props.object.contentType == "function"'>
|
|
||||||
<li t-on-click="() => this.store.inspectFunctionSource(this.props.object.path)" class="custom-menu-item py-1 px-4 text-nowrap">Inspect function source code</li>
|
|
||||||
</t>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<t t-if="props.object.toggled" t-key="contextMenuId">
|
<t t-if="props.object.toggled" t-key="contextMenuId">
|
||||||
<t t-foreach="props.object.children" t-as="child" t-key="child_index">
|
<t t-foreach="props.object.children" t-as="child" t-key="child_index">
|
||||||
<ObjectTreeElement object="child" class="this.classFor(child)"/>
|
<ObjectTreeElement object="child" class="this.classFor(child)"/>
|
||||||
|
|||||||
+79
-14
@@ -16,10 +16,7 @@ export class TreeElement extends Component {
|
|||||||
searched: false,
|
searched: false,
|
||||||
});
|
});
|
||||||
this.store = useStore();
|
this.store = useStore();
|
||||||
this.contextMenu = useRef("contextmenu");
|
|
||||||
this.element = useRef("element");
|
this.element = useRef("element");
|
||||||
this.contextMenuId = this.store.contextMenu.id++;
|
|
||||||
this.contextMenuEvent;
|
|
||||||
this.stringifiedPath = JSON.stringify(this.props.component.path);
|
this.stringifiedPath = JSON.stringify(this.props.component.path);
|
||||||
// Scroll to the selected element when it changes
|
// Scroll to the selected element when it changes
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -38,15 +35,6 @@ export class TreeElement extends Component {
|
|||||||
},
|
},
|
||||||
() => [this.props.component.selected]
|
() => [this.props.component.selected]
|
||||||
);
|
);
|
||||||
// Open the context menu when the ids match
|
|
||||||
useEffect(
|
|
||||||
(menuId) => {
|
|
||||||
if (menuId === this.contextMenuId) {
|
|
||||||
this.store.contextMenu.open(this.contextMenuEvent, this.contextMenu.el);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => [this.store.contextMenu.activeMenu]
|
|
||||||
);
|
|
||||||
// Effect to apply a short highlight effect to the component when it is rendered
|
// Effect to apply a short highlight effect to the component when it is rendered
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
@@ -86,9 +74,86 @@ export class TreeElement extends Component {
|
|||||||
return minimizeKey(this.props.component.key);
|
return minimizeKey(this.props.component.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get contextMenuItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Expand children",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.toggleComponentAndChildren(this.props.component, true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fold all children",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.toggleComponentAndChildren(this.props.component, false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fold direct children",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.foldDirectChildren(this.props.component),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect source code",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.inspectComponent("source", this.props.component.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: this.props.component.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.props.component.path,
|
||||||
|
{ type: "item", value: "component" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect in Elements tab",
|
||||||
|
show: this.props.component.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("DOM", this.props.component.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Force rerender",
|
||||||
|
show: this.props.component.path.length !== 1,
|
||||||
|
action: () => this.store.refreshComponent(this.props.component.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store observed states as global variable",
|
||||||
|
show: this.props.component.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.props.component.path,
|
||||||
|
{ type: "item", value: "subscriptions" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect compiled template",
|
||||||
|
show: this.props.component.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("compiled template", this.props.component.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Log raw template",
|
||||||
|
show: this.props.component.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("raw template", this.props.component.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: this.props.component.path.length === 1,
|
||||||
|
action: () => this.store.logObjectInConsole([...this.props.component.path]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Don't fold component by default",
|
||||||
|
show: this.store.settings.componentsToggleBlacklist.has(this.props.component.name),
|
||||||
|
action: () => this.toggleComponentToBlacklist(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fold component by default",
|
||||||
|
show: !this.store.settings.componentsToggleBlacklist.has(this.props.component.name),
|
||||||
|
action: () => this.toggleComponentToBlacklist(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
openMenu(ev) {
|
openMenu(ev) {
|
||||||
this.contextMenuEvent = ev;
|
this.store.openContextMenu(ev, this.contextMenuItems);
|
||||||
this.store.contextMenu.activeMenu = this.contextMenuId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand/fold the component node
|
// Expand/fold the component node
|
||||||
|
|||||||
+5
-28
@@ -2,15 +2,15 @@
|
|||||||
<templates xml:space="preserve">
|
<templates xml:space="preserve">
|
||||||
<t t-name="devtools.TreeElement" owl="1">
|
<t t-name="devtools.TreeElement" owl="1">
|
||||||
<div t-ref="element"
|
<div t-ref="element"
|
||||||
t-att-class="{'component-selected': props.component.selected,'component-highlighted': props.component.highlighted}"
|
t-att-class="{'component-selected': props.component.selected,'component-highlighted': props.component.highlighted}"
|
||||||
class="tree-component m-0 p-0 w-100 text-nowrap user-select-none"
|
class="tree-component m-0 p-0 w-100 text-nowrap user-select-none"
|
||||||
t-on-contextmenu.prevent="openMenu"
|
t-on-contextmenu.prevent="openMenu"
|
||||||
t-on-mouseover.stop="() => this.store.highlightComponent(props.component.path)"
|
t-on-mouseover.stop="() => this.store.highlightComponent(props.component.path)"
|
||||||
t-on-click.stop="toggleComponent"
|
t-on-click.stop="toggleComponent"
|
||||||
>
|
>
|
||||||
<div class="component-wrapper" t-attf-style="padding-left: {{componentPadding}}rem">
|
<div class="component-wrapper" t-attf-style="padding-left: {{componentPadding}}rem">
|
||||||
<i class="fa px-1 pointer-icon caret"
|
<i class="fa px-1 pointer-icon caret"
|
||||||
t-att-class="{'fa-caret-right': !props.component.toggled, 'fa-caret-down': props.component.toggled}"
|
t-att-class="{'fa-caret-right': !props.component.toggled, 'fa-caret-down': props.component.toggled}"
|
||||||
t-on-click.stop="toggleDisplay"
|
t-on-click.stop="toggleDisplay"
|
||||||
t-attf-style="{{props.component.children.length > 0 ? '' : 'visibility: hidden;'}}"
|
t-attf-style="{{props.component.children.length > 0 ? '' : 'visibility: hidden;'}}"
|
||||||
/>
|
/>
|
||||||
@@ -26,29 +26,6 @@
|
|||||||
<span t-if="props.component.depth">></span>
|
<span t-if="props.component.depth">></span>
|
||||||
<span class="version" t-else="">owl=<t t-esc="props.component.version"/></span>
|
<span class="version" t-else="">owl=<t t-esc="props.component.version"/></span>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="store.contextMenu.activeMenu === contextMenuId" class="custom-menu" t-ref="contextmenu">
|
|
||||||
<ul class="my-1">
|
|
||||||
<li t-on-click.stop="() => this.store.toggleComponentAndChildren(props.component, true)" class="custom-menu-item py-1 px-4">Expand children</li>
|
|
||||||
<li t-on-click.stop="() => this.store.toggleComponentAndChildren(props.component, false)" class="custom-menu-item py-1 px-4">Fold all children</li>
|
|
||||||
<li t-on-click.stop="() => this.store.foldDirectChildren(props.component)" class="custom-menu-item py-1 px-4">Fold direct children</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('source', props.component.path)" class="custom-menu-item py-1 px-4">Inspect source code</li>
|
|
||||||
<t t-if="props.component.path.length !== 1">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.component.path, { type: 'item', value: 'component'}])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('DOM', props.component.path)" class="custom-menu-item py-1 px-4">Inspect in Elements tab</li>
|
|
||||||
<li t-on-click.stop="() => this.store.refreshComponent(props.component.path)" class="custom-menu-item py-1 px-4">Force rerender</li>
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.component.path, { type: 'item', value: 'subscriptions'}])" class="custom-menu-item py-1 px-4">Store observed states as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('compiled template', props.component.path)" class="custom-menu-item py-1 px-4">Inspect compiled template</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('raw template', props.component.path)" class="custom-menu-item py-1 px-4">Log raw template</li>
|
|
||||||
</t>
|
|
||||||
<t t-else="">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.component.path])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
</t>
|
|
||||||
<li t-on-click.stop="() => this.toggleComponentToBlacklist()" class="custom-menu-item py-1 px-4">
|
|
||||||
<t t-if="store.settings.componentsToggleBlacklist.has(props.component.name)">Don't fold component by default</t>
|
|
||||||
<t t-else="">Fold component by default</t>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<t t-if="props.component.toggled">
|
<t t-if="props.component.toggled">
|
||||||
<t t-foreach="props.component.children" t-as="child" t-key="child.key">
|
<t t-foreach="props.component.children" t-as="child" t-key="child.key">
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
const { Component } = owl;
|
const { Component } = owl;
|
||||||
import { ComponentsTab } from "./components_tab/components_tab";
|
import { ContextMenu } from "../context_menu/context_menu";
|
||||||
import { Tab } from "./tab/tab";
|
|
||||||
import { ProfilerTab } from "./profiler_tab/profiler_tab";
|
|
||||||
import { useStore } from "../store/store";
|
import { useStore } from "../store/store";
|
||||||
|
import { ComponentsTab } from "./components_tab/components_tab";
|
||||||
|
import { ProfilerTab } from "./profiler_tab/profiler_tab";
|
||||||
|
import { Tab } from "./tab/tab";
|
||||||
|
|
||||||
export class DevtoolsWindow extends Component {
|
export class DevtoolsWindow extends Component {
|
||||||
static props = [];
|
static props = [];
|
||||||
static template = "devtools.DevtoolsWindow";
|
static template = "devtools.DevtoolsWindow";
|
||||||
static components = { ComponentsTab, Tab, ProfilerTab };
|
static components = { ComponentsTab, Tab, ProfilerTab, ContextMenu };
|
||||||
setup() {
|
setup() {
|
||||||
this.store = useStore();
|
this.store = useStore();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
Owl is not loaded on this page.
|
Owl is not loaded on this page.
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
<ContextMenu t-if="store.contextMenu" items="store.contextMenu.items"/>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
import { minimizeKey } from "../../../../utils";
|
import { minimizeKey } from "../../../../utils";
|
||||||
import { useStore } from "../../../store/store";
|
import { useStore } from "../../../store/store";
|
||||||
|
|
||||||
const { Component, useEffect, useRef } = owl;
|
const { Component } = owl;
|
||||||
|
|
||||||
export class Event extends Component {
|
export class Event extends Component {
|
||||||
static template = "devtools.Event";
|
static template = "devtools.Event";
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
this.store = useStore();
|
this.store = useStore();
|
||||||
this.componentContextMenu = useRef("componentContextmenu");
|
|
||||||
this.componentContextMenuId = this.store.contextMenu.id++;
|
|
||||||
this.contextMenuEvent,
|
|
||||||
useEffect(
|
|
||||||
(menuId) => {
|
|
||||||
if (menuId === this.componentContextMenuId) {
|
|
||||||
this.store.contextMenu.open(this.contextMenuEvent, this.componentContextMenu.el);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => [this.store.contextMenu.activeMenu]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formatting for displaying the key of the component
|
// Formatting for displaying the key of the component
|
||||||
@@ -54,13 +43,65 @@ export class Event extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openComponentMenu(ev) {
|
get contextMenuItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Inspect source code",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.inspectComponent("source", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.props.event.path,
|
||||||
|
{ type: "item", value: "component" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect in Elements tab",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("DOM", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Force rerender",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.refreshComponent(this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store observed states as global variable",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.props.event.path,
|
||||||
|
{ type: "item", value: "subscriptions" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect compiled template",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("compiled template", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Log raw template",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("raw template", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: this.props.event.path.length === 1,
|
||||||
|
action: () => this.store.logObjectInConsole([...this.props.event.path]),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
openMenu(ev) {
|
||||||
if (this.props.event.type === "destroy") {
|
if (this.props.event.type === "destroy") {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.contextMenuEvent = ev;
|
this.store.openContextMenu(ev, this.contextMenuItems);
|
||||||
this.store.contextMenu.activeMenu = this.componentContextMenuId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@
|
|||||||
t-att-class="{'fa-caret-right': !props.event.toggled, 'fa-caret-down': props.event.toggled}"
|
t-att-class="{'fa-caret-right': !props.event.toggled, 'fa-caret-down': props.event.toggled}"
|
||||||
t-attf-style="visibility: {{props.event.origin ? '' : 'hidden'}};"
|
t-attf-style="visibility: {{props.event.origin ? '' : 'hidden'}};"
|
||||||
/>
|
/>
|
||||||
<t t-esc="props.event.type"/>:
|
<t t-esc="props.event.type"/>:
|
||||||
<<span style="cursor:pointer; color: var(--component-color);"
|
<<span style="cursor:pointer; color: var(--component-color);"
|
||||||
t-on-click.stop="() => this.store.selectComponent(props.event.path)"
|
t-on-click.stop="() => this.store.selectComponent(props.event.path)"
|
||||||
t-on-mouseover.stop="() => this.store.highlightComponent(props.event.path)"
|
t-on-mouseover.stop="() => this.store.highlightComponent(props.event.path)"
|
||||||
t-on-contextmenu="openComponentMenu"
|
t-on-contextmenu="openMenu"
|
||||||
t-esc="props.event.component"
|
t-esc="props.event.component"
|
||||||
/>
|
/>
|
||||||
<t t-if="minimizedKey.length > 0">
|
<t t-if="minimizedKey.length > 0">
|
||||||
@@ -29,10 +29,10 @@
|
|||||||
<div class="my-0 pt-1 object-line">
|
<div class="my-0 pt-1 object-line">
|
||||||
<i class="fa fa-caret-right mx-1 pe-2" style="visibility: hidden;"></i>
|
<i class="fa fa-caret-right mx-1 pe-2" style="visibility: hidden;"></i>
|
||||||
<span>
|
<span>
|
||||||
origin:
|
origin:
|
||||||
<<span style="cursor:pointer; color: var(--component-color);"
|
<<span style="cursor:pointer; color: var(--component-color);"
|
||||||
t-on-click.stop="() => this.store.selectComponent(props.event.origin.path)"
|
t-on-click.stop="() => this.store.selectComponent(props.event.origin.path)"
|
||||||
t-on-mouseover.stop="() => this.store.highlightComponent(props.event.origin.path)"
|
t-on-mouseover.stop="() => this.store.highlightComponent(props.event.origin.path)"
|
||||||
t-esc="props.event.origin.component"
|
t-esc="props.event.origin.component"
|
||||||
/>
|
/>
|
||||||
<t t-if="originMinimizedKey.length > 0">
|
<t t-if="originMinimizedKey.length > 0">
|
||||||
@@ -43,22 +43,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
<div t-if="store.contextMenu.activeMenu === componentContextMenuId" class="custom-menu" t-ref="componentContextmenu">
|
|
||||||
<ul class="my-1">
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('source', props.event.path)" class="custom-menu-item py-1 px-4">Inspect source code</li>
|
|
||||||
<t t-if="props.event.path.length !== 1">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.event.path, { type: 'item', value: 'component'}])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('DOM', props.event.path)" class="custom-menu-item py-1 px-4">Inspect in Elements tab</li>
|
|
||||||
<li t-on-click.stop="() => this.store.refreshComponent(props.event.path)" class="custom-menu-item py-1 px-4">Force rerender</li>
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.event.path, { type: 'item', value: 'subscriptions'}])" class="custom-menu-item py-1 px-4">Store observed states as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('compiled template', props.event.path)" class="custom-menu-item py-1 px-4">Inspect compiled template</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('raw template', props.event.path)" class="custom-menu-item py-1 px-4">Log raw template</li>
|
|
||||||
</t>
|
|
||||||
<t t-else="">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.event.path])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
</t>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
+76
-21
@@ -1,7 +1,7 @@
|
|||||||
import { minimizeKey } from "../../../../utils";
|
import { minimizeKey } from "../../../../utils";
|
||||||
import { useStore } from "../../../store/store";
|
import { useStore } from "../../../store/store";
|
||||||
|
|
||||||
const { Component, useRef, useEffect } = owl;
|
const { Component } = owl;
|
||||||
|
|
||||||
export class EventNode extends Component {
|
export class EventNode extends Component {
|
||||||
static template = "devtools.EventNode";
|
static template = "devtools.EventNode";
|
||||||
@@ -10,33 +10,89 @@ export class EventNode extends Component {
|
|||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
this.store = useStore();
|
this.store = useStore();
|
||||||
this.nodeContextMenu = useRef("nodeContextMenu");
|
|
||||||
this.nodeContextMenuId = this.store.contextMenu.id++;
|
|
||||||
this.componentContextMenu = useRef("componentContextmenu");
|
|
||||||
this.componentContextMenuId = this.store.contextMenu.id++;
|
|
||||||
this.contextMenuEvent,
|
|
||||||
useEffect(
|
|
||||||
(menuId) => {
|
|
||||||
if (menuId === this.nodeContextMenuId) {
|
|
||||||
this.store.contextMenu.open(this.contextMenuEvent, this.nodeContextMenu.el);
|
|
||||||
}
|
|
||||||
if (menuId === this.componentContextMenuId) {
|
|
||||||
this.store.contextMenu.open(this.contextMenuEvent, this.componentContextMenu.el);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => [this.store.contextMenu.activeMenu]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get eventPadding() {
|
get eventPadding() {
|
||||||
return this.props.event.depth * 0.8 + 0.3;
|
return this.props.event.depth * 0.8 + 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get nodeContextMenuItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Expand children",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.toggleEventAndChildren(this.props.event, true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fold all children",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.toggleEventAndChildren(this.props.event, false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fold direct children",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.foldDirectChildren(this.props.event),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get componentContextMenuItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Inspect source code",
|
||||||
|
show: true,
|
||||||
|
action: () => this.store.inspectComponent("source", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.props.event.path,
|
||||||
|
{ type: "item", value: "component" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect in Elements tab",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("DOM", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Force rerender",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.refreshComponent(this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store observed states as global variable",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () =>
|
||||||
|
this.store.logObjectInConsole([
|
||||||
|
...this.props.event.path,
|
||||||
|
{ type: "item", value: "subscriptions" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Inspect compiled template",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("compiled template", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Log raw template",
|
||||||
|
show: this.props.event.path.length !== 1,
|
||||||
|
action: () => this.store.inspectComponent("raw template", this.props.event.path),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Store as global variable",
|
||||||
|
show: this.props.event.path.length === 1,
|
||||||
|
action: () => this.store.logObjectInConsole([...this.props.event.path]),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
openNodeMenu(ev) {
|
openNodeMenu(ev) {
|
||||||
if (this.props.event.children.length) {
|
if (this.props.event.children.length) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.contextMenuEvent = ev;
|
this.store.openContextMenu(ev, this.nodeContextMenuItems);
|
||||||
this.store.contextMenu.activeMenu = this.nodeContextMenuId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +101,7 @@ export class EventNode extends Component {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.contextMenuEvent = ev;
|
this.store.openContextMenu(ev, this.componentContextMenuItems);
|
||||||
this.store.contextMenu.activeMenu = this.componentContextMenuId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-30
@@ -2,8 +2,8 @@
|
|||||||
<templates xml:space="preserve">
|
<templates xml:space="preserve">
|
||||||
<t t-name="devtools.EventNode" owl="1">
|
<t t-name="devtools.EventNode" owl="1">
|
||||||
<div class="my-0 p-0 object-line"
|
<div class="my-0 p-0 object-line"
|
||||||
t-on-click.stop="toggleDisplay"
|
t-on-click.stop="toggleDisplay"
|
||||||
t-on-contextmenu="openNodeMenu"
|
t-on-contextmenu="openNodeMenu"
|
||||||
>
|
>
|
||||||
<div class="text-nowrap" t-attf-style="padding-left: {{eventPadding}}rem">
|
<div class="text-nowrap" t-attf-style="padding-left: {{eventPadding}}rem">
|
||||||
<i class="fa px-1 pointer-icon caret"
|
<i class="fa px-1 pointer-icon caret"
|
||||||
@@ -11,11 +11,11 @@
|
|||||||
t-attf-style="visibility: {{props.event.children.length > 0 ? '' : 'hidden'}};"
|
t-attf-style="visibility: {{props.event.children.length > 0 ? '' : 'hidden'}};"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
<t t-esc="props.event.type"/>:
|
<t t-esc="props.event.type"/>:
|
||||||
<<span style="cursor:pointer; color: var(--component-color);"
|
<<span style="cursor:pointer; color: var(--component-color);"
|
||||||
t-on-click.stop="() => this.store.selectComponent(props.event.path)"
|
t-on-click.stop="() => this.store.selectComponent(props.event.path)"
|
||||||
t-on-mouseover.stop="() => this.store.highlightComponent(props.event.path)"
|
t-on-mouseover.stop="() => this.store.highlightComponent(props.event.path)"
|
||||||
t-on-contextmenu.stop="openComponentMenu"
|
t-on-contextmenu.stop="openComponentMenu"
|
||||||
t-esc="props.event.component"/>
|
t-esc="props.event.component"/>
|
||||||
<t t-if="minimizedKey.length > 0">
|
<t t-if="minimizedKey.length > 0">
|
||||||
<span t-if="minimizedKey.length > 0" style="color: var(--key-name);"> key</span>=<span style="color: var(--key-content);">
|
<span t-if="minimizedKey.length > 0" style="color: var(--key-name);"> key</span>=<span style="color: var(--key-content);">
|
||||||
@@ -28,29 +28,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="store.contextMenu.activeMenu === nodeContextMenuId" class="custom-menu" t-ref="nodeContextMenu">
|
|
||||||
<ul class="my-1">
|
|
||||||
<li t-on-click.stop="() => this.store.toggleEventAndChildren(props.event, true)" class="custom-menu-item py-1 px-4">Expand children</li>
|
|
||||||
<li t-on-click.stop="() => this.store.toggleEventAndChildren(props.event, false)" class="custom-menu-item py-1 px-4">Fold all children</li>
|
|
||||||
<li t-on-click.stop="() => this.store.foldDirectChildren(props.event)" class="custom-menu-item py-1 px-4">Fold direct children</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div t-if="store.contextMenu.activeMenu === componentContextMenuId" class="custom-menu" t-ref="componentContextmenu">
|
|
||||||
<ul class="my-1">
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('source', props.event.path)" class="custom-menu-item py-1 px-4">Inspect source code</li>
|
|
||||||
<t t-if="props.event.path.length !== 1">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.event.path, { type: 'item', value: 'component'}])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('DOM', props.event.path)" class="custom-menu-item py-1 px-4">Inspect in Elements tab</li>
|
|
||||||
<li t-on-click.stop="() => this.store.refreshComponent(props.event.path)" class="custom-menu-item py-1 px-4">Force rerender</li>
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.event.path, { type: 'item', value: 'subscriptions'}])" class="custom-menu-item py-1 px-4">Store observed states as global variable</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('compiled template', props.event.path)" class="custom-menu-item py-1 px-4">Inspect compiled template</li>
|
|
||||||
<li t-on-click.stop="() => this.store.inspectComponent('raw template', props.event.path)" class="custom-menu-item py-1 px-4">Log raw template</li>
|
|
||||||
</t>
|
|
||||||
<t t-else="">
|
|
||||||
<li t-on-click.stop="() => this.store.logObjectInConsole([...props.event.path])" class="custom-menu-item py-1 px-4">Store as global variable</li>
|
|
||||||
</t>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<t t-if="props.event.toggled">
|
<t t-if="props.event.toggled">
|
||||||
<t t-foreach="props.event.children" t-as="child" t-key="child.id">
|
<t t-foreach="props.event.children" t-as="child" t-key="child.id">
|
||||||
<EventNode event="child"/>
|
<EventNode event="child"/>
|
||||||
|
|||||||
@@ -11,30 +11,7 @@ export const store = reactive({
|
|||||||
darkmode: false,
|
darkmode: false,
|
||||||
componentsToggleBlacklist: new Set(),
|
componentsToggleBlacklist: new Set(),
|
||||||
},
|
},
|
||||||
contextMenu: {
|
contextMenu: null,
|
||||||
id: 0,
|
|
||||||
activeMenu: -1,
|
|
||||||
// Opens the context menu corresponding with the given menu html element
|
|
||||||
open(event, menu) {
|
|
||||||
const menuWidth = menu.offsetWidth;
|
|
||||||
const menuHeight = menu.offsetHeight;
|
|
||||||
let x = event.clientX;
|
|
||||||
let y = event.clientY;
|
|
||||||
if (x + menuWidth > window.innerWidth) {
|
|
||||||
x = window.innerWidth - menuWidth;
|
|
||||||
}
|
|
||||||
if (y + menuHeight > window.innerHeight) {
|
|
||||||
y = window.innerHeight - menuHeight;
|
|
||||||
}
|
|
||||||
menu.style.left = x + "px";
|
|
||||||
// Need 25px offset because of the main navbar from the browser devtools
|
|
||||||
menu.style.top = y - 25 + "px";
|
|
||||||
},
|
|
||||||
// Close the currently displayed context menu
|
|
||||||
close() {
|
|
||||||
this.activeMenu = -1;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isFirefox: IS_FIREFOX,
|
isFirefox: IS_FIREFOX,
|
||||||
frameUrls: ["top"],
|
frameUrls: ["top"],
|
||||||
activeFrame: "top",
|
activeFrame: "top",
|
||||||
@@ -93,6 +70,16 @@ export const store = reactive({
|
|||||||
evalFunctionInWindow("disableHTMLSelector", [], this.activeFrame);
|
evalFunctionInWindow("disableHTMLSelector", [], this.activeFrame);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openContextMenu(event, items) {
|
||||||
|
this.contextMenu = {
|
||||||
|
position: {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY,
|
||||||
|
},
|
||||||
|
items,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
// Load all data related to the components tree using the global hook loaded on the page
|
// Load all data related to the components tree using the global hook loaded on the page
|
||||||
// Use fromOld to specify if we want to keep most of the toggled/selected data of the old tree
|
// Use fromOld to specify if we want to keep most of the toggled/selected data of the old tree
|
||||||
// when generating the new one
|
// when generating the new one
|
||||||
@@ -646,8 +633,9 @@ async function init() {
|
|||||||
store.updateIFrameList();
|
store.updateIFrameList();
|
||||||
|
|
||||||
// Global listeners to close the currently shown context menu when the user clicks or opens another
|
// Global listeners to close the currently shown context menu when the user clicks or opens another
|
||||||
document.addEventListener("click", () => store.contextMenu.close(), { capture: true });
|
document.addEventListener("click", () => (store.contextMenu = null), { capture: true });
|
||||||
document.addEventListener("contextmenu", () => store.contextMenu.close(), { capture: true });
|
document.addEventListener("contextmenu", () => (store.contextMenu = null), { capture: true });
|
||||||
|
window.addEventListener("blur", () => (store.contextMenu = null), { capture: true });
|
||||||
|
|
||||||
// Make sure the events recorder is at its initial state in every frame
|
// Make sure the events recorder is at its initial state in every frame
|
||||||
for (const frame of store.frameUrls) {
|
for (const frame of store.frameUrls) {
|
||||||
|
|||||||
Reference in New Issue
Block a user