[inula-dev-tools]<feat> 信息组件合入
This commit is contained in:
parent
8c559f644d
commit
6b59405236
|
@ -0,0 +1,279 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openInula is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import 'assets.less';
|
||||||
|
|
||||||
|
.infoContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #5f6673;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button :hover {
|
||||||
|
color: #23272f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.componentInfoHead {
|
||||||
|
flex: 0 0 @top-height;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: @divider-style;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: 0 1rem 0 1rem;
|
||||||
|
.text {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.eye {
|
||||||
|
flex: 0 0 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug {
|
||||||
|
flex: 0 0 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.25rem 0.5rem 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.location {
|
||||||
|
flex: 0 0 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.25rem 0.5rem 0.25rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.componentInfoMain {
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
border-bottom: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
border-bottom: @divider-style;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attrContainer {
|
||||||
|
flex: 0 0;
|
||||||
|
|
||||||
|
.attrHead {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem 0.5rem 0 0.5rem;
|
||||||
|
|
||||||
|
.attrType {
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attrCopy {
|
||||||
|
flex: 0 0 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attrDetail {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
|
||||||
|
.attrArrow {
|
||||||
|
color: @arrow-color;
|
||||||
|
width: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attrName {
|
||||||
|
margin-top: 1px;
|
||||||
|
color: @attr-name-color;
|
||||||
|
font-family: @attr-name-font-family;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colon {
|
||||||
|
margin-top: 1px;
|
||||||
|
transform: translateY(-8%);
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
&:hover {
|
||||||
|
.operation {
|
||||||
|
visibility: visible;
|
||||||
|
.operationIcon :hover {
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: lightskyblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attrValue {
|
||||||
|
width: 26rem;
|
||||||
|
height: 1rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo,
|
||||||
|
Courier, monospace;
|
||||||
|
&:focus {
|
||||||
|
color: unset;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attrValue[data-type='string'] {
|
||||||
|
color: #009906;
|
||||||
|
}
|
||||||
|
.attrValue[data-type='function'] {
|
||||||
|
color: royalblue;
|
||||||
|
}
|
||||||
|
.attrValue[data-type='number'] {
|
||||||
|
color: #ff5722;
|
||||||
|
}
|
||||||
|
.attrValue[data-type='boolean'] {
|
||||||
|
color: #03a9f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
cursor: pointer;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkBox {
|
||||||
|
margin: 2px 3px 0 auto;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown.active {
|
||||||
|
display: unset;
|
||||||
|
top: var(--content-top);
|
||||||
|
left: var(--content-left);
|
||||||
|
position: absolute;
|
||||||
|
ul {
|
||||||
|
margin-block-start: 0;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
li {
|
||||||
|
padding: 10px;
|
||||||
|
border-top: 1px lighten(#333, 2%) solid;
|
||||||
|
height: auto;
|
||||||
|
overflow: auto;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 0 10px;
|
||||||
|
background: darken(#333, 2%);
|
||||||
|
color: darken(#EEE, 40%);
|
||||||
|
text-align: left;
|
||||||
|
border: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
transition-property: all, background-color;
|
||||||
|
transition-duration: 0.2s, 0.4s;
|
||||||
|
|
||||||
|
&:hover, &.selected {
|
||||||
|
background-color: darken(#333, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #03a9f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-radius: 5px 5px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
&:before {
|
||||||
|
content: url('../svgs/copy.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
&:before {
|
||||||
|
content: url('../svgs/storage.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.parentsInfo {
|
||||||
|
flex: 1 1 0;
|
||||||
|
|
||||||
|
.parentName {
|
||||||
|
padding: 0.5rem 0.5rem 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent {
|
||||||
|
margin-left: 1.4rem;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
color: @component-name-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @select-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,435 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openInula is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import styles from './ComponentInfo.less';
|
||||||
|
import Eye from '../svgs/Eye';
|
||||||
|
import Debug from '../svgs/Debug';
|
||||||
|
import Location from '../svgs/Location';
|
||||||
|
import Triangle from '../svgs/Triangle';
|
||||||
|
import { memo, useContext, useEffect, useState, useRef, useMemo, createRef } from 'openinula';
|
||||||
|
import { IData } from './VTree';
|
||||||
|
import { buildAttrModifyData, IAttr } from '../parser/parseAttr';
|
||||||
|
import { postMessageToBackground } from '../panelConnection';
|
||||||
|
import { CopyToConsole, InspectDom, LogComponentData, ModifyAttrs, StorageValue } from '../utils/constants';
|
||||||
|
import type { Source } from '../../../inula/src/renderer/Types';
|
||||||
|
import ViewSourceContext from '../utils/ViewSource';
|
||||||
|
import PickElementContext from '../utils/PickElement';
|
||||||
|
import Operation from '../svgs/Operation';
|
||||||
|
|
||||||
|
type IComponentInfo = {
|
||||||
|
name: string;
|
||||||
|
attrs: {
|
||||||
|
parsedProps?: IAttr[];
|
||||||
|
parsedState?: IAttr[];
|
||||||
|
parsedHooks?: IAttr[];
|
||||||
|
};
|
||||||
|
parents: IData[];
|
||||||
|
id: number;
|
||||||
|
source?: Source;
|
||||||
|
onClickParent: (item: IData) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ComponentAttr = memo(function ComponentAttr({
|
||||||
|
attrsName,
|
||||||
|
attrsType,
|
||||||
|
attrs,
|
||||||
|
id,
|
||||||
|
dropdownRef,
|
||||||
|
}: {
|
||||||
|
attrsName: string;
|
||||||
|
attrsType: string;
|
||||||
|
attrs: IAttr[];
|
||||||
|
id: number;
|
||||||
|
dropdownRef: null | HTMLElement;
|
||||||
|
}) {
|
||||||
|
const [editableAttrs, setEditableAttrs] = useState(attrs);
|
||||||
|
const [expandNodes, setExpandNodes] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setEditableAttrs(attrs);
|
||||||
|
}, [attrs]);
|
||||||
|
|
||||||
|
const handleCollapse = (item: IAttr) => {
|
||||||
|
const nodes = [...expandNodes];
|
||||||
|
const expandItem = `${item.name}_${editableAttrs.indexOf(item)}`;
|
||||||
|
const i = nodes.indexOf(expandItem);
|
||||||
|
if (i === -1) {
|
||||||
|
nodes.push(expandItem);
|
||||||
|
} else {
|
||||||
|
nodes.splice(i, 1);
|
||||||
|
}
|
||||||
|
setExpandNodes(nodes);
|
||||||
|
};
|
||||||
|
|
||||||
|
// props 展示的 key: value 中的 value 值
|
||||||
|
const getShowName = item => {
|
||||||
|
let retStr;
|
||||||
|
if (item === undefined) {
|
||||||
|
retStr = String(item);
|
||||||
|
} else if (typeof item === 'number') {
|
||||||
|
retStr = item;
|
||||||
|
} else if (typeof item === 'string') {
|
||||||
|
retStr = item.endsWith('>') ? `<${item}` : item;
|
||||||
|
} else {
|
||||||
|
retStr = `"${item}"`;
|
||||||
|
}
|
||||||
|
return retStr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拿到 props 或 hooks 在 VNode 里的路径
|
||||||
|
*
|
||||||
|
* @param {Array<IAttr>} editableAttrs 所有 props 与 hooks 的值
|
||||||
|
* @param {number} index 此值在 editableAttrs 的下标位置
|
||||||
|
* @param {string} attrsType 此值属于 props 还是 hooks
|
||||||
|
* @return {Array} 值在 vNode 里的路径
|
||||||
|
*/
|
||||||
|
const getPath = (editableAttrs: IAttr[], index: number, attrsType: string): Array<string | number> => {
|
||||||
|
const path: Array<string | number> = [];
|
||||||
|
let local = editableAttrs[index].indentation;
|
||||||
|
if (local === 1) {
|
||||||
|
path.push(attrsType === 'Hooks' ? editableAttrs[index].hIndex : editableAttrs[index].name);
|
||||||
|
} else {
|
||||||
|
let location = local;
|
||||||
|
let id = index;
|
||||||
|
while (location > 0) {
|
||||||
|
// local === 1 时处于 vNode.hooks 的子元素最外层
|
||||||
|
if (location < local || id === index || local === 1) {
|
||||||
|
if (local === 1) {
|
||||||
|
attrsType === 'Hooks'
|
||||||
|
? path.unshift(editableAttrs[id + 1].hIndex, 'state')
|
||||||
|
: path.unshift(editableAttrs[id + 1].name);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (editableAttrs[id]?.indentation === 1) {
|
||||||
|
if (editableAttrs[id]?.name === 'State') {
|
||||||
|
path.unshift('stateValue');
|
||||||
|
}
|
||||||
|
if (editableAttrs[id]?.name === 'Ref') {
|
||||||
|
path.unshift('current');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path.unshift(editableAttrs[id].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 跳过同级
|
||||||
|
local = location;
|
||||||
|
}
|
||||||
|
location = id >= 1 ? editableAttrs[id - 1].indentation : -1;
|
||||||
|
id = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAttr = [];
|
||||||
|
let currentIndentation = null;
|
||||||
|
|
||||||
|
// 为每一行数据添加一个 ref
|
||||||
|
const refsById = useMemo(() => {
|
||||||
|
const refs = {};
|
||||||
|
editableAttrs.forEach((item, index) => {
|
||||||
|
refs[index] = createRef();
|
||||||
|
});
|
||||||
|
return refs;
|
||||||
|
}, [editableAttrs]);
|
||||||
|
|
||||||
|
editableAttrs.forEach((item, index) => {
|
||||||
|
const operationRef = refsById[index];
|
||||||
|
const indentation = item.indentation;
|
||||||
|
if (currentIndentation !== null) {
|
||||||
|
if (indentation > currentIndentation) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
currentIndentation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const nextItem = editableAttrs[index + 1];
|
||||||
|
const hasChild = nextItem ? nextItem.indentation - item.indentation > 0 : false;
|
||||||
|
const isCollapsed = !expandNodes.includes(`${item.name}_${index}`);
|
||||||
|
|
||||||
|
// 按钮点击事件
|
||||||
|
const operationClick = (e: Event, operationRef: any) => {
|
||||||
|
// 防止点击按钮触发展开或者合起数据
|
||||||
|
e.stopPropagation();
|
||||||
|
if (operationRef.current) {
|
||||||
|
const operationRect = operationRef.current.getBoundingClientRect();
|
||||||
|
// 19.2 为图标按钮高度,85 为弹框高度的一半
|
||||||
|
dropdownRef.style.setProperty('--content-top', `${operationRect.top + 19.2}px`);
|
||||||
|
dropdownRef.style.setProperty('--content-left', `${operationRect.left - 85}px`);
|
||||||
|
}
|
||||||
|
dropdownRef.classList.toggle(styles['active']);
|
||||||
|
const attrInfo = {
|
||||||
|
id: { id },
|
||||||
|
itemName: item.name,
|
||||||
|
attrsName: attrsName,
|
||||||
|
path: getPath(editableAttrs, index, attrsName),
|
||||||
|
};
|
||||||
|
(dropdownRef as any).attrInfo = attrInfo;
|
||||||
|
console.log(dropdownRef);
|
||||||
|
};
|
||||||
|
|
||||||
|
showAttr.push(
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
style={{ paddingLeft: item.indentation * 10 }}
|
||||||
|
key={index}
|
||||||
|
onclick={() => handleCollapse(item)}
|
||||||
|
>
|
||||||
|
<span className={styles.attrArrow}>{hasChild && <Triangle director={isCollapsed ? 'right' : 'down'} />}</span>
|
||||||
|
<span className={styles.attrName}>{`${item.name}`}</span>
|
||||||
|
<div className={styles.colon}>{':'}</div>
|
||||||
|
{item.type === 'string' || item.type === 'number' || item.type === 'undefined' || item.type === 'null' ? (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
value={getShowName(item.value)}
|
||||||
|
data-type={item.type}
|
||||||
|
className={styles.attrValue}
|
||||||
|
onChange={event => {
|
||||||
|
const nextAttrs = [...editableAttrs];
|
||||||
|
const nextItem = { ...item };
|
||||||
|
nextItem.value = event.target.value;
|
||||||
|
nextAttrs[index] = nextItem;
|
||||||
|
setEditableAttrs(nextAttrs);
|
||||||
|
}}
|
||||||
|
onKeyUp={event => {
|
||||||
|
const value = (event.target as HTMLInputElement).value;
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
if (isDev) {
|
||||||
|
console.log('post attr change', value);
|
||||||
|
} else {
|
||||||
|
const data = buildAttrModifyData(attrsType, attrs, value, item, index, id);
|
||||||
|
postMessageToBackground(ModifyAttrs, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className={styles.operation} ref={operationRef}>
|
||||||
|
<span className={styles.operationIcon} onclick={event => operationClick(event, operationRef)}>
|
||||||
|
<Operation />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : item.type === 'boolean' ? (
|
||||||
|
<>
|
||||||
|
<span data-type={item.type} className={styles.attrValue}>
|
||||||
|
{item.value.toString()}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type={'checkbox'}
|
||||||
|
checked={item.value}
|
||||||
|
className={styles.checkBox}
|
||||||
|
onChange={event => {
|
||||||
|
const nextAttrs = [...editableAttrs];
|
||||||
|
const nextItem = { ...item };
|
||||||
|
nextItem.value = event.target.checked;
|
||||||
|
nextAttrs[index] = nextItem;
|
||||||
|
setEditableAttrs(nextAttrs);
|
||||||
|
if (!isDev) {
|
||||||
|
const data = buildAttrModifyData(attrsType, attrs, nextItem.value, item, index, id);
|
||||||
|
postMessageToBackground(ModifyAttrs, data);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-type={item.type} className={styles.attrValue}>
|
||||||
|
{item.value}
|
||||||
|
</span>
|
||||||
|
<div className={styles.operation} ref={operationRef}>
|
||||||
|
<span className={styles.operationIcon} onClick={event => operationClick(event, operationRef)}>
|
||||||
|
<Operation />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
if (isCollapsed) {
|
||||||
|
currentIndentation = indentation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.attrContainer}>
|
||||||
|
<div className={styles.attrHead}>
|
||||||
|
<span className={styles.attrType}>{attrsName}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.attrDetail}>{showAttr}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function ComponentInfo({ name, attrs, parents, id, source, onClickParent }: IComponentInfo) {
|
||||||
|
const view = useContext(ViewSourceContext) as any;
|
||||||
|
const viewSource = view.viewSourceFunction.viewSource;
|
||||||
|
|
||||||
|
const pick = useContext(PickElementContext) as any;
|
||||||
|
const inspectVNode = pick.pickElementFunction.inspectVNode;
|
||||||
|
const dropdownRef = useRef<null | HTMLElement>(null);
|
||||||
|
|
||||||
|
const doViewSource = (id: number) => {
|
||||||
|
postMessageToBackground(InspectDom, { id });
|
||||||
|
setTimeout(function () {
|
||||||
|
inspectVNode();
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const doInspectDom = (id: number) => {
|
||||||
|
postMessageToBackground(InspectDom, { id });
|
||||||
|
setTimeout(function () {
|
||||||
|
inspectVNode();
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourceFormatted = (fileName: string, lineNumber: number) => {
|
||||||
|
const pathWithoutLastName = /^(.*)[\\/]/;
|
||||||
|
|
||||||
|
let realName = fileName.replace(pathWithoutLastName, '');
|
||||||
|
if (/^index\./.test(realName)) {
|
||||||
|
const fileNameMatch = fileName.match(pathWithoutLastName);
|
||||||
|
if (fileNameMatch) {
|
||||||
|
const pathBeforeName = fileNameMatch[1];
|
||||||
|
if (pathBeforeName) {
|
||||||
|
const folderName = pathBeforeName.replace(pathWithoutLastName, '');
|
||||||
|
realName = folderName + '/' + realName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${realName}:${lineNumber}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyToConsole = (itemName: string | number, attrsName: string, path: Array<string | number>) => {
|
||||||
|
postMessageToBackground(CopyToConsole, { id, itemName, attrsName, path });
|
||||||
|
dropdownRef.current.classList.toggle(styles['active']);
|
||||||
|
};
|
||||||
|
|
||||||
|
const storeVariable = (attrsName: string, path: Array<string | number>) => {
|
||||||
|
postMessageToBackground(StorageValue, { id, attrsName, path });
|
||||||
|
dropdownRef.current.classList.toggle(styles['active']);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.infoContainer}>
|
||||||
|
<div className={styles.componentInfoHead}>
|
||||||
|
{name && (
|
||||||
|
<>
|
||||||
|
<div className={styles.name}>
|
||||||
|
<div className={styles.text}>{name}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className={styles.button}>
|
||||||
|
<span
|
||||||
|
className={styles.eye}
|
||||||
|
title={'Inspect dom element'}
|
||||||
|
onClick={() => {
|
||||||
|
doInspectDom(id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Eye />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button className={styles.button} disabled={false}>
|
||||||
|
<span
|
||||||
|
className={styles.location}
|
||||||
|
onClick={() => {
|
||||||
|
doViewSource(id);
|
||||||
|
}}
|
||||||
|
title={'View source for this element'}
|
||||||
|
>
|
||||||
|
<Location />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button className={styles.button}>
|
||||||
|
<span
|
||||||
|
className={styles.debug}
|
||||||
|
title={'Log this component data'}
|
||||||
|
onClick={() => {
|
||||||
|
postMessageToBackground(LogComponentData, id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Debug />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.componentInfoMain}>
|
||||||
|
{Object.keys(attrs).map(attrsType => {
|
||||||
|
const parsedAttrs = attrs[attrsType];
|
||||||
|
if (parsedAttrs && parsedAttrs.length !== 0) {
|
||||||
|
const attrsName = attrsType.slice(6); // parsedState => State
|
||||||
|
return (
|
||||||
|
<ComponentAttr
|
||||||
|
attrsName={attrsName}
|
||||||
|
attrsType={attrsType}
|
||||||
|
attrs={parsedAttrs}
|
||||||
|
id={id}
|
||||||
|
dropdownRef={dropdownRef.current}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
<div className={styles.parentsInfo}>
|
||||||
|
{name && (
|
||||||
|
<div>
|
||||||
|
<div className={styles.parentName}>Parents</div>
|
||||||
|
{parents.map(item => (
|
||||||
|
<button className={styles.parent} onClick={() => onClickParent(item)}>
|
||||||
|
{`<${item.name.itemName}>`}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.parentsInfo}>
|
||||||
|
{source && (
|
||||||
|
<>
|
||||||
|
<div>source: {''}</div>
|
||||||
|
<div style={{ marginLeft: '1rem' }}>{sourceFormatted(source.fileName, source.lineNumber)}</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div ref={dropdownRef} className={styles.dropdown}>
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
onClick={() =>
|
||||||
|
copyToConsole(
|
||||||
|
(dropdownRef.current as any).attrInfo.itemName,
|
||||||
|
(dropdownRef.current as any).attrInfo.attrsName,
|
||||||
|
(dropdownRef.current as any).attrInfo.path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<b>Copy value to console</b>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
onClick={() => storeVariable((dropdownRef.current as any).attrInfo.attrsName, (dropdownRef.current as any).attrInfo.path)}
|
||||||
|
>
|
||||||
|
<b>Store as global variable</b>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ComponentInfo);
|
Loading…
Reference in New Issue