[inula-dev-tools]<feat> injector 逻辑合入
This commit is contained in:
parent
8f33376722
commit
559a6fc3aa
|
@ -0,0 +1,485 @@
|
||||||
|
/*
|
||||||
|
* 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 parseTreeRoot, { clearVNode, queryVNode, VNodeToIdMap } from '../parser/parseVNode';
|
||||||
|
import { packagePayload, checkMessage } from '../utils/transferUtils';
|
||||||
|
import {
|
||||||
|
RequestAllVNodeTreeInfos,
|
||||||
|
AllVNodeTreeInfos,
|
||||||
|
RequestComponentAttrs,
|
||||||
|
ComponentAttrs,
|
||||||
|
DevToolHook,
|
||||||
|
DevToolContentScript,
|
||||||
|
ModifyAttrs,
|
||||||
|
ModifyHooks,
|
||||||
|
ModifyState,
|
||||||
|
ModifyProps,
|
||||||
|
InspectDom,
|
||||||
|
LogComponentData,
|
||||||
|
Highlight,
|
||||||
|
RemoveHighlight,
|
||||||
|
ViewSource,
|
||||||
|
PickElement,
|
||||||
|
StopPickElement,
|
||||||
|
CopyToConsole,
|
||||||
|
StorageValue,
|
||||||
|
} from '../utils/constants';
|
||||||
|
import { VNode } from '../../../inula/src/renderer/vnode/VNode';
|
||||||
|
import { parseVNodeAttrs } from '../parser/parseAttr';
|
||||||
|
import { showHighlight, hideHighlight } from '../highlight';
|
||||||
|
import {
|
||||||
|
FunctionComponent,
|
||||||
|
ClassComponent,
|
||||||
|
IncompleteClassComponent,
|
||||||
|
ForwardRef,
|
||||||
|
MemoComponent
|
||||||
|
} from '../../../inula/src/renderer/vnode/VNodeTags';
|
||||||
|
import { pickElement } from './pickElement';
|
||||||
|
|
||||||
|
const roots = [];
|
||||||
|
let storeDataCount = 0;
|
||||||
|
|
||||||
|
function addIfNotInclude(treeRoot: VNode) {
|
||||||
|
if (!roots.includes(treeRoot)) {
|
||||||
|
roots.push(treeRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function send() {
|
||||||
|
const result = roots.reduce((pre, current) => {
|
||||||
|
const info = parseTreeRoot(helper.travelVNodeTree, current);
|
||||||
|
pre.push(info);
|
||||||
|
return pre;
|
||||||
|
}, []);
|
||||||
|
postMessage(AllVNodeTreeInfos, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteVNode(vNode: VNode) {
|
||||||
|
// 开发工具中保存了 vNode 的引用,在清理 vNode 的时候需要一并删除
|
||||||
|
clearVNode(vNode);
|
||||||
|
const index = roots.indexOf(vNode);
|
||||||
|
if (index !== -1) {
|
||||||
|
roots.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postMessage(type: string, data) {
|
||||||
|
window.postMessage(
|
||||||
|
packagePayload(
|
||||||
|
{
|
||||||
|
type: type,
|
||||||
|
data: data,
|
||||||
|
},
|
||||||
|
DevToolHook
|
||||||
|
),
|
||||||
|
'*'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCompAttrs(id: number) {
|
||||||
|
const vNode = queryVNode(id);
|
||||||
|
if (!vNode) {
|
||||||
|
console.error('Do not find match vNode, this is a bug, please report us.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parsedAttrs = parseVNodeAttrs(vNode, helper.getHookInfo);
|
||||||
|
postMessage(ComponentAttrs, parsedAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateNextValue(editValue, value, attrPath) {
|
||||||
|
let nextState;
|
||||||
|
const editValueType = typeof editValue;
|
||||||
|
if (
|
||||||
|
editValueType === 'string' ||
|
||||||
|
editValueType === 'undefined' ||
|
||||||
|
editValueType === 'boolean'
|
||||||
|
) {
|
||||||
|
nextState = value;
|
||||||
|
} else if (editValueType === 'number') {
|
||||||
|
const numValue = Number(value);
|
||||||
|
nextState = isNaN(numValue) ? value : numValue; // 如果能转为数字,转数字,不能转数字旧用原值
|
||||||
|
} else if (editValueType === 'object') {
|
||||||
|
if (editValue === null) {
|
||||||
|
nextState = value;
|
||||||
|
} else {
|
||||||
|
const newValue = Array.isArray(editValue) ? [...editValue] : { ...editValue };
|
||||||
|
// 遍历读取到直接指向需要修改值的对象
|
||||||
|
let attr = newValue;
|
||||||
|
for (let i = 0; i < attrPath.length - 1; i++) {
|
||||||
|
attr = attr[attrPath[i]];
|
||||||
|
}
|
||||||
|
// 修改对象上的值
|
||||||
|
attr[attrPath[attrPath.length - 1]] = value;
|
||||||
|
nextState = newValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
'The dev tools tried to edit a non-editable value, this is a bug, please report.',
|
||||||
|
editValue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
function modifyVNodeAttrs(data) {
|
||||||
|
const { type, id, value, path } = data;
|
||||||
|
const vNode = queryVNode(id);
|
||||||
|
if (!vNode) {
|
||||||
|
console.error('Do not find match vNode, this is a bug, please report us.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === ModifyProps) {
|
||||||
|
const nextProps = calculateNextValue(vNode.props, value, path);
|
||||||
|
helper.updateProps(vNode, nextProps);
|
||||||
|
} else if (type === ModifyHooks) {
|
||||||
|
const hooks = vNode.hooks;
|
||||||
|
const editHook = hooks[path[0]];
|
||||||
|
const hookInfo = helper.getHookInfo(editHook);
|
||||||
|
if (hookInfo) {
|
||||||
|
const editValue = hookInfo.value;
|
||||||
|
// path 的第一个指向 hIndex,从第二个值才开始指向具体属性访问路径
|
||||||
|
const nextState = calculateNextValue(editValue, value, path.slice(1));
|
||||||
|
helper.updateHooks(vNode, path[0], nextState);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
'The dev tools tried to edit a non-editable hook, this is a bug, please report.',
|
||||||
|
hooks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (type === ModifyState) {
|
||||||
|
const oldState = vNode.state || {};
|
||||||
|
const nextState = { ...oldState };
|
||||||
|
let accessRef = nextState;
|
||||||
|
for (let i = 0; i < path.length - 1; i++) {
|
||||||
|
accessRef = accessRef[path[i]];
|
||||||
|
}
|
||||||
|
accessRef[path[path.length - 1]] = value;
|
||||||
|
helper.updateState(vNode, nextState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logComponentData(id: number) {
|
||||||
|
const vNode = queryVNode(id);
|
||||||
|
if (vNode == null) {
|
||||||
|
console.warn(`Could not find vNode with id "${id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (vNode) {
|
||||||
|
const info = helper.getComponentInfo(vNode);
|
||||||
|
console.log('vNode: ', vNode);
|
||||||
|
console.log('Component Info: ', info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 path 在 vNode 拿到对应的值
|
||||||
|
*
|
||||||
|
* @param {VNode} vNode dom 节点
|
||||||
|
* @param {Array<string | number>} path 路径
|
||||||
|
* @param {string} attrsName 值的类型(props 或者 hooks)
|
||||||
|
*/
|
||||||
|
const getValueByPath = (
|
||||||
|
vNode: VNode,
|
||||||
|
path: Array<string | number>,
|
||||||
|
attrsName: string
|
||||||
|
) => {
|
||||||
|
if (attrsName === 'Props') {
|
||||||
|
return path.reduce((previousValue, currentValue) => {
|
||||||
|
return previousValue[currentValue];
|
||||||
|
}, vNode.props);
|
||||||
|
} else {
|
||||||
|
// attrsName 为 Hooks
|
||||||
|
if (path.length > 1) {
|
||||||
|
return path.reduce((previousValue, currentValue) => {
|
||||||
|
return previousValue[currentValue];
|
||||||
|
}, vNode.hooks);
|
||||||
|
}
|
||||||
|
return vNode.hooks[path[0]];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 path 在 vNode 拿到对应的值,并且在控制台打印出来
|
||||||
|
*
|
||||||
|
* @param {number} id idToVNodeMap 的 key 值,通过 id 拿到 VNode
|
||||||
|
* @param {string} itemName 打印出来值的名称
|
||||||
|
* @param {Array<string | number>} path 值的路径
|
||||||
|
* @param {string} attrsName 值的类型
|
||||||
|
*/
|
||||||
|
function logDataWithPath(
|
||||||
|
id: number,
|
||||||
|
itemName: string,
|
||||||
|
path: Array<string | number>,
|
||||||
|
attrsName: string
|
||||||
|
) {
|
||||||
|
const vNode = queryVNode(id);
|
||||||
|
if (vNode === null) {
|
||||||
|
console.warn(`Could not find vNode with id "${id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (vNode) {
|
||||||
|
const value = getValueByPath(vNode, path, attrsName);
|
||||||
|
if (attrsName === 'Hooks') {
|
||||||
|
console.log(itemName, value);
|
||||||
|
} else {
|
||||||
|
console.log(`${path[path.length - 1]}`, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 path 在 vNode 拿到对应的值,并且存为全局变量
|
||||||
|
*
|
||||||
|
* @param {number} id idToVNodeMap 的 key 值,通过 id 拿到 VNode
|
||||||
|
* @param {Array<string |number>} path 值的路径
|
||||||
|
* @param {string} attrsName 值的类型
|
||||||
|
*/
|
||||||
|
function storeDataWithPath(
|
||||||
|
id: number,
|
||||||
|
path: Array<string | number>,
|
||||||
|
attrsName: string
|
||||||
|
) {
|
||||||
|
const vNode = queryVNode(id);
|
||||||
|
if (vNode === null) {
|
||||||
|
console.warn(`Could not find vNode with id "${id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (vNode) {
|
||||||
|
const value = getValueByPath(vNode, path, attrsName);
|
||||||
|
const key = `$InulaTemp${storeDataCount++}`;
|
||||||
|
|
||||||
|
window[key] = value;
|
||||||
|
console.log(key);
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let helper;
|
||||||
|
|
||||||
|
function init(inulaHelper) {
|
||||||
|
helper = inulaHelper;
|
||||||
|
(window as any).__INULA_DEV_HOOK__.isInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElement(travelVNodeTree, treeRoot: VNode) {
|
||||||
|
const result: any[] = [];
|
||||||
|
travelVNodeTree(
|
||||||
|
treeRoot,
|
||||||
|
(node: VNode) => {
|
||||||
|
if (node.realNode) {
|
||||||
|
if (Object.keys(node.realNode).length > 0 || node.realNode.size > 0) {
|
||||||
|
result.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(node: VNode) =>
|
||||||
|
node.realNode != null &&
|
||||||
|
(Object.keys(node.realNode).length > 0 || node.realNode.size > 0)
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dev tools 点击眼睛图标功能
|
||||||
|
const inspectDom = data => {
|
||||||
|
const { id } = data;
|
||||||
|
const vNode = queryVNode(id);
|
||||||
|
if (vNode == null) {
|
||||||
|
console.warn(`Could not find vNode with id "${id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const info = getElement(helper.travelVNodeTree, vNode);
|
||||||
|
if (info) {
|
||||||
|
showHighlight(info);
|
||||||
|
(window as any).__INULA_DEV_HOOK__.$0 = info[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const picker = pickElement(window);
|
||||||
|
|
||||||
|
const actions = new Map([
|
||||||
|
// 请求左树所有数据
|
||||||
|
[
|
||||||
|
RequestAllVNodeTreeInfos,
|
||||||
|
() => {
|
||||||
|
send();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 请求某个节点的 props,hooks
|
||||||
|
[
|
||||||
|
RequestComponentAttrs,
|
||||||
|
data => {
|
||||||
|
parseCompAttrs(data);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 修改 props,hooks
|
||||||
|
[
|
||||||
|
ModifyAttrs,
|
||||||
|
data => {
|
||||||
|
modifyVNodeAttrs(data);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 找到节点对应 element
|
||||||
|
[
|
||||||
|
InspectDom,
|
||||||
|
data => {
|
||||||
|
inspectDom(data);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 打印节点数据
|
||||||
|
[
|
||||||
|
LogComponentData,
|
||||||
|
data => {
|
||||||
|
logComponentData(data);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 高亮
|
||||||
|
[
|
||||||
|
Highlight,
|
||||||
|
data => {
|
||||||
|
const node = queryVNode(data.id);
|
||||||
|
if (node == null) {
|
||||||
|
console.warn(`Could not find vNode with id "${data.id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const info = getElement(helper.travelVNodeTree, node);
|
||||||
|
showHighlight(info);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 移出高亮
|
||||||
|
[
|
||||||
|
RemoveHighlight,
|
||||||
|
() => {
|
||||||
|
hideHighlight();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 查看节点源代码位置
|
||||||
|
[
|
||||||
|
ViewSource,
|
||||||
|
data => {
|
||||||
|
const node = queryVNode(data.id);
|
||||||
|
if (node == null) {
|
||||||
|
console.warn(`Could not find vNode with id "${data.id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
showSource(node);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 选中页面元素对应 dev tools 节点
|
||||||
|
[
|
||||||
|
PickElement,
|
||||||
|
() => {
|
||||||
|
picker.startPick();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
StopPickElement,
|
||||||
|
() => {
|
||||||
|
picker.stopPick();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 在控制台打印 Props Hooks State 值
|
||||||
|
[
|
||||||
|
CopyToConsole,
|
||||||
|
data => {
|
||||||
|
const node = queryVNode(data.id);
|
||||||
|
if (node == null) {
|
||||||
|
console.warn(`Could not find vNode with id "${data.id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
logDataWithPath(data.id, data.itemName, data.path, data.attrsName);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 把 Props Hooks State 值存为全局变量
|
||||||
|
[
|
||||||
|
StorageValue,
|
||||||
|
data => {
|
||||||
|
const node = queryVNode(data.id);
|
||||||
|
if (node == null) {
|
||||||
|
console.warn(`Could not find vNode with id "${data.id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
storeDataWithPath(data.id, data.path, data.attrsName);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const showSource = (node: VNode) => {
|
||||||
|
switch (node.tag) {
|
||||||
|
case ClassComponent:
|
||||||
|
case IncompleteClassComponent:
|
||||||
|
case FunctionComponent:
|
||||||
|
global.$type = node.type;
|
||||||
|
break;
|
||||||
|
case ForwardRef:
|
||||||
|
global.$type = node.type.render;
|
||||||
|
break;
|
||||||
|
case MemoComponent:
|
||||||
|
global.$type = node.type.type;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
global.$type = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRequest = (type: string, data) => {
|
||||||
|
const action = actions.get(type);
|
||||||
|
if (action) {
|
||||||
|
action.call(this, data);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
console.warn('unknown command', type);
|
||||||
|
};
|
||||||
|
|
||||||
|
function injectHook() {
|
||||||
|
if ((window as any).__INULA_DEV_HOOK__) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.defineProperty(window, '__INULA_DEV_HOOK__', {
|
||||||
|
enumerable: false,
|
||||||
|
value: {
|
||||||
|
$0: null,
|
||||||
|
init,
|
||||||
|
isInit: false,
|
||||||
|
addIfNotInclude,
|
||||||
|
send,
|
||||||
|
deleteVNode,
|
||||||
|
// inulaX 使用
|
||||||
|
getVNodeId: vNode => {
|
||||||
|
return VNodeToIdMap.get(vNode);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
// 只接收我们自己的消息
|
||||||
|
if (event.source !== window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const request = event.data;
|
||||||
|
if (checkMessage(request, DevToolContentScript)) {
|
||||||
|
const { payload } = request;
|
||||||
|
const { type, data } = payload;
|
||||||
|
|
||||||
|
// 忽略 inulaX 的 actions
|
||||||
|
if (type.startsWith('inulax')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleRequest(type, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
injectHook();
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* 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 { PickElement, StopPickElement } from '../utils/constants';
|
||||||
|
import { getElement, helper, postMessage } from './index';
|
||||||
|
import { queryVNode, VNodeToIdMap } from '../parser/parseVNode';
|
||||||
|
import { isUserComponent } from '../parser/parseVNode';
|
||||||
|
import { throttle } from 'lodash';
|
||||||
|
import { hideHighlight, showHighlight } from '../highlight';
|
||||||
|
|
||||||
|
// 判断鼠标移入节点是否为 dev tools 上的节点,如果不是则找父节点
|
||||||
|
function getUserComponent(target) {
|
||||||
|
if (target.tag && isUserComponent(target.tag)) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
while (target.tag && !isUserComponent(target.tag)) {
|
||||||
|
if (target.parent) {
|
||||||
|
target = target.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseEvent(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseMove(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const target = (event.target as any)._inula_VNode;
|
||||||
|
if (target) {
|
||||||
|
const id = VNodeToIdMap.get(getUserComponent(target));
|
||||||
|
const vNode = queryVNode(id);
|
||||||
|
if (vNode == null) {
|
||||||
|
console.warn(`Could not find vNode with id "${id}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const info = getElement(helper.travelVNodeTree, vNode);
|
||||||
|
if (info) {
|
||||||
|
showHighlight(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0.5 秒内在节流结束后只触发一次
|
||||||
|
throttle(
|
||||||
|
() => {
|
||||||
|
postMessage(PickElement, id);
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
{ leading: false, trailing: true }
|
||||||
|
)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pickElement(window: Window) {
|
||||||
|
function onClick(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
stopPick();
|
||||||
|
postMessage(StopPickElement, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startPick = () => {
|
||||||
|
if (window && typeof window.addEventListener === 'function') {
|
||||||
|
window.addEventListener('click', onClick, true);
|
||||||
|
window.addEventListener('mousedown', onMouseEvent, true);
|
||||||
|
window.addEventListener('mousemove', onMouseMove, true);
|
||||||
|
window.addEventListener('mouseup', onMouseEvent, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopPick = () => {
|
||||||
|
hideHighlight();
|
||||||
|
window.removeEventListener('click', onClick, true);
|
||||||
|
window.removeEventListener('mousedown', onMouseEvent, true);
|
||||||
|
window.removeEventListener('mousemove', onMouseMove, true);
|
||||||
|
window.removeEventListener('mouseup', onMouseEvent, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { startPick, stopPick };
|
||||||
|
}
|
Loading…
Reference in New Issue