refactor(editor): 新增tree组件

This commit is contained in:
roymondchen 2023-11-01 14:06:04 +08:00
parent 8d8c8df82f
commit 7d251f04e8
13 changed files with 453 additions and 329 deletions

View File

@ -0,0 +1,59 @@
<template>
<div class="m-editor-tree" @dragover="handleDragOver">
<TreeNode v-for="item in data" :key="item.id" :data="item" :indent="indent" :node-status-map="nodeStatusMap">
<template #tree-node-content="{ data: nodeData }">
<slot name="tree-node-content" :data="nodeData"> </slot>
</template>
<template #tree-node-tool="{ data: nodeData }">
<slot name="tree-node-tool" :data="nodeData"> </slot>
</template>
</TreeNode>
</div>
</template>
<script setup lang="ts">
import { provide } from 'vue';
import type { Id } from '@tmagic/schema';
import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
import TreeNode from './TreeNode.vue';
defineSlots<{
'tree-node-content'(props: { data: TreeNodeData }): any;
'tree-node-tool'(props: { data: TreeNodeData }): any;
}>();
defineOptions({
name: 'MEditorTree',
});
const emit = defineEmits<{
'node-dragover': [event: DragEvent];
'node-dragstart': [event: DragEvent, data: TreeNodeData];
'node-dragleave': [event: DragEvent, data: TreeNodeData];
'node-dragend': [event: DragEvent, data: TreeNodeData];
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
'node-mouseenter': [event: MouseEvent, data: TreeNodeData];
'node-click': [event: MouseEvent, data: TreeNodeData];
}>();
provide('treeEmit', emit);
withDefaults(
defineProps<{
data: TreeNodeData[];
nodeStatusMap: Map<Id, LayerNodeStatus>;
indent?: number;
}>(),
{
indent: 0,
},
);
const handleDragOver = (event: DragEvent) => {
emit('node-dragover', event);
};
</script>

View File

@ -0,0 +1,142 @@
<template>
<div
v-show="visible"
class="m-editor-tree-node"
:draggable="draggable"
:data-node-id="data.id"
:data-is-container="Array.isArray(data.items)"
@dragstart="handleDragStart"
@dragleave="handleDragLeave"
@dragend="handleDragEnd"
>
<div
class="tree-node"
:class="{ selected, expanded }"
:style="`padding-left: ${indent}px`"
@contextmenu="nodeContentmenuHandler"
@mouseenter="mouseenterHandler"
>
<MIcon
class="expand-icon"
:style="hasChilren ? '' : 'color: transparent; cursor: default'"
:icon="expanded ? ArrowDown : ArrowRight"
@click="expandHandler"
></MIcon>
<div class="tree-node-content" @click="nodeClickHandler">
<slot name="tree-node-content" :data="data">
<div class="tree-node-label">{{ `${data.name} (${data.id})` }}</div>
<div class="tree-node-tool">
<slot name="tree-node-tool" :data="data"></slot>
</div>
</slot>
</div>
</div>
<div v-if="hasChilren && expanded" class="m-editor-tree-node-children">
<TreeNode
v-for="item in data.items"
:key="item.id"
:data="item"
:node-status-map="nodeStatusMap"
:indent="indent + 11"
>
<template #tree-node-content="{ data: nodeData }">
<slot name="tree-node-content" :data="nodeData"> </slot>
</template>
<template #tree-node-tool="{ data: nodeData }">
<slot name="tree-node-tool" :data="nodeData"> </slot>
</template>
</TreeNode>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
import type { Id } from '@tmagic/schema';
import MIcon from '@editor/components/Icon.vue';
import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
import { updateStatus } from '@editor/utils/tree';
defineSlots<{
'tree-node-tool'(props: { data: TreeNodeData }): any;
'tree-node-content'(props: { data: TreeNodeData }): any;
}>();
defineOptions({
name: 'MEditorTreeNode',
});
const emit = defineEmits<{
'node-dragstart': [event: DragEvent, data: TreeNodeData];
'node-dragleave': [event: DragEvent, data: TreeNodeData];
'node-dragend': [event: DragEvent, data: TreeNodeData];
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
'node-mouseenter': [event: MouseEvent, data: TreeNodeData];
'node-click': [event: MouseEvent, data: TreeNodeData];
}>();
const treeEmit = inject<typeof emit>('treeEmit');
const props = withDefaults(
defineProps<{
data: TreeNodeData;
nodeStatusMap: Map<Id, LayerNodeStatus>;
indent?: number;
}>(),
{
indent: 0,
},
);
const nodeStatus = computed(
() =>
props.nodeStatusMap?.get(props.data.id) || {
selected: false,
expand: false,
visible: false,
draggable: false,
},
);
const expanded = computed(() => nodeStatus.value.expand);
const selected = computed(() => nodeStatus.value.selected);
const visible = computed(() => nodeStatus.value.visible);
const draggable = computed(() => nodeStatus.value.draggable);
const hasChilren = computed(() => props.data.items && props.data.items.length > 0);
const handleDragStart = (event: DragEvent) => {
treeEmit?.('node-dragstart', event, props.data);
};
const handleDragLeave = (event: DragEvent) => {
treeEmit?.('node-dragleave', event, props.data);
};
const handleDragEnd = (event: DragEvent) => {
treeEmit?.('node-dragend', event, props.data);
};
const nodeContentmenuHandler = (event: MouseEvent) => {
treeEmit?.('node-contextmenu', event, props.data);
};
const mouseenterHandler = (event: MouseEvent) => {
treeEmit?.('node-mouseenter', event, props.data);
};
const expandHandler = () => {
updateStatus(props.nodeStatusMap, props.data.id, {
expand: !expanded.value,
});
};
const nodeClickHandler = (event: MouseEvent) => {
treeEmit?.('node-click', event, props.data);
};
</script>

View File

@ -1,208 +0,0 @@
<template>
<div
v-show="visible"
class="magic-editor-layer-node"
ref="nodeEl"
:draggable="!isPage(data)"
:data-node-id="data.id"
:data-is-container="Array.isArray(data.items)"
@dragstart="handleDragStart"
@dragleave="handleDragLeave"
@dragend="handleDragEnd($event, data)"
>
<div
class="layer-node"
:class="{ selected, expanded }"
:style="`padding-left: ${indent}px`"
@contextmenu="contextmenuHandler"
@mouseenter="highlightHandler()"
>
<MIcon
class="expand-icon"
:style="hasChilren ? '' : 'color: transparent; cursor: default'"
:icon="expanded ? ArrowDown : ArrowRight"
@click="expandHandler"
></MIcon>
<div class="layer-node-content" @click="nodeClickHandler">
<slot name="layer-node-content" :data="data">
<div class="layer-node-label">{{ `${data.name} (${data.id})` }}</div>
<div class="layer-node-tool">
<LayerNodeTool :data="data"></LayerNodeTool>
</div>
</slot>
</div>
</div>
<div v-if="hasChilren && expanded" class="magic-editor-layer-node-children">
<LayerNode
v-for="item in data.items"
:data="item"
:parent="(data as MContainer)"
:key="item.id"
:indent="indent + 11"
:filter-text="filterText"
:is-ctrl-key-down="isCtrlKeyDown"
@node-contextmenu="nodeContentmenuHandler"
>
<template #layer-node-content="{ data: nodeData }">
<slot name="layer-node-content" :data="nodeData"> </slot>
</template>
</LayerNode>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, type ComputedRef, inject, nextTick, ref } from 'vue';
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
import { throttle } from 'lodash-es';
import type { Id, MContainer, MNode } from '@tmagic/schema';
import { isPage } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import { LayerNodeSlots, LayerNodeStatus, Services, UI_SELECT_MODE_EVENT_NAME } from '@editor/type';
import LayerNodeTool from './LayerNodeTool.vue';
import { useDrag } from './use-drag';
import { updateStatus } from './use-filter';
defineSlots<LayerNodeSlots>();
defineOptions({
name: 'MEditorLayerNode',
});
const props = withDefaults(
defineProps<{
data: MNode;
parent?: MContainer;
indent?: number;
filterText?: string;
isCtrlKeyDown?: boolean;
}>(),
{
indent: 0,
filterText: '',
isCtrlKeyDown: false,
},
);
const emit = defineEmits<{
'node-contextmenu': [event: MouseEvent, node: MNode];
}>();
const services = inject<Services>('services');
const nodeStatusMap = inject<ComputedRef<Map<Id, LayerNodeStatus>>>('nodeStatusMap');
const editorService = services?.editorService;
const uiService = services?.uiService;
const nodeStatus = computed(
() =>
nodeStatusMap?.value?.get(props.data.id) || {
selected: false,
expand: false,
visible: false,
},
);
const expanded = computed(() => nodeStatus.value.expand);
const selected = computed(() => nodeStatus.value.selected);
const visible = computed(() => nodeStatus.value.visible);
const hasChilren = computed(() => props.data.items?.length > 0);
const nodeEl = ref<HTMLDivElement>();
const { handleDragStart, handleDragEnd, handleDragLeave } = useDrag(services);
const expandHandler = () => {
if (!nodeStatusMap?.value) return;
updateStatus(nodeStatusMap.value, props.data.id, {
expand: !expanded.value,
});
};
const nodeClickHandler = () => {
if (!nodeStatusMap?.value) return;
if (uiService?.get('uiSelectMode')) {
document.dispatchEvent(new CustomEvent(UI_SELECT_MODE_EVENT_NAME, { detail: props.data }));
return;
}
if (hasChilren.value && !props.isCtrlKeyDown) {
updateStatus(nodeStatusMap.value, props.data.id, {
expand: true,
});
}
nextTick(() => {
select(props.data);
});
};
const contextmenuHandler = (event: MouseEvent) => {
event.preventDefault();
const nodes = editorService?.get('nodes') || [];
if (nodes.length < 2 || !nodes.includes(props.data)) {
nodeClickHandler();
}
emit('node-contextmenu', event, props.data);
};
const nodeContentmenuHandler = (event: MouseEvent, node: MNode) => {
emit('node-contextmenu', event, node);
};
//
const select = async (data: MNode) => {
if (!data.id) {
throw new Error('没有id');
}
if (props.isCtrlKeyDown) {
multiSelect(data);
} else {
await editorService?.select(data);
editorService?.get('stage')?.select(data.id);
}
};
const multiSelect = async (data: MNode) => {
const nodes = editorService?.get('nodes') || [];
const newNodes: Id[] = [];
let isCancel = false;
nodes.forEach((node) => {
if (node.id === data.id) {
isCancel = true;
return;
}
newNodes.push(node.id);
});
//
if (!isCancel || newNodes.length === 0) {
newNodes.push(data.id);
}
await editorService?.multiSelect(newNodes);
editorService?.get('stage')?.multiSelect(newNodes);
};
const throttleTime = 300;
//
const highlightHandler = throttle(() => {
highlight();
}, throttleTime);
//
const highlight = () => {
editorService?.highlight(props.data);
editorService?.get('stage')?.highlight(props.data.id);
};
</script>

View File

@ -1,22 +1,31 @@
<template>
<TMagicScrollbar class="magic-editor-layer-panel">
<TMagicScrollbar class="m-editor-layer-panel">
<slot name="layer-panel-header"></slot>
<SearchInput @search="filterTextChangeHandler"></SearchInput>
<div class="magic-editor-layer-tree" ref="layerTreeContainer" tabindex="-1" @dragover="handleDragOver">
<LayerNode
v-if="page && root"
:data="page"
:filter-text="filterText"
:is-ctrl-key-down="isCtrlKeyDown"
@node-contextmenu="contextmenu"
>
<template #layer-node-content="{ data: nodeData }">
<slot name="layer-node-content" :data="nodeData"> </slot>
</template>
</LayerNode>
</div>
<Tree
v-if="page && nodeStatusMap"
tabindex="-1"
ref="tree"
:data="[page]"
:node-status-map="nodeStatusMap"
@node-dragover="handleDragOver"
@node-dragstart="handleDragStart"
@node-dragleave="handleDragLeave"
@node-dragend="handleDragEnd"
@node-contextmenu="nodeContentmenuHandler"
@node-mouseenter="mouseenterHandler"
@node-click="nodeClickHandler"
>
<template #tree-node-tool="{ data: nodeData }">
<LayerNodeTool :data="nodeData"></LayerNodeTool>
</template>
<template #tree-node-content="{ data: nodeData }">
<slot name="layer-node-content" :data="nodeData"> </slot>
</template>
</Tree>
<Teleport to="body">
<LayerMenu ref="menu" :layer-content-menu="layerContentMenu" @collapse-all="collapseAllHandler"></LayerMenu>
@ -25,15 +34,17 @@
</template>
<script setup lang="ts">
import { computed, inject, provide, ref } from 'vue';
import { computed, inject, ref } from 'vue';
import { TMagicScrollbar } from '@tmagic/design';
import SearchInput from '@editor/components/SearchInput.vue';
import type { LayerPanelSlots, MenuButton, MenuComponent, Services } from '@editor/type';
import Tree from '@editor/components/Tree.vue';
import { LayerPanelSlots, MenuButton, MenuComponent, Services } from '@editor/type';
import LayerMenu from './LayerMenu.vue';
import LayerNode from './LayerNode.vue';
import LayerNodeTool from './LayerNodeTool.vue';
import { useClick } from './use-click';
import { useDrag } from './use-drag';
import { useFilter } from './use-filter';
import { useKeybinding } from './use-keybinding';
@ -52,17 +63,14 @@ defineProps<{
const services = inject<Services>('services');
const editorService = services?.editorService;
const layerTreeContainer = ref<HTMLDivElement>();
const tree = ref<InstanceType<typeof Tree>>();
const page = computed(() => editorService?.get('page'));
const root = computed(() => editorService?.get('root'));
const { nodeStatusMap } = useNodeStatus(services, page);
const { isCtrlKeyDown } = useKeybinding(services, layerTreeContainer);
const { isCtrlKeyDown } = useKeybinding(services, tree);
provide('nodeStatusMap', nodeStatusMap);
const { filterText, filterTextChangeHandler } = useFilter(nodeStatusMap, page);
const { filterTextChangeHandler } = useFilter(nodeStatusMap, page);
const collapseAllHandler = () => {
if (!page.value || !nodeStatusMap.value) return;
@ -75,13 +83,12 @@ const collapseAllHandler = () => {
}
};
//
const menu = ref<InstanceType<typeof LayerMenu>>();
const contextmenu = (event: MouseEvent): void => {
event.preventDefault();
const { handleDragStart, handleDragEnd, handleDragLeave, handleDragOver } = useDrag(services);
menu.value?.show(event);
};
const { handleDragOver } = useDrag(services);
const {
menu,
nodeClickHandler,
nodeContentmenuHandler,
highlightHandler: mouseenterHandler,
} = useClick(services, isCtrlKeyDown, nodeStatusMap);
</script>

View File

@ -0,0 +1,105 @@
import { type ComputedRef, nextTick, type Ref, ref } from 'vue';
import { throttle } from 'lodash-es';
import { Id, MNode } from '@tmagic/schema';
import { LayerNodeStatus, Services, TreeNodeData, UI_SELECT_MODE_EVENT_NAME } from '@editor/type';
import { updateStatus } from '@editor/utils/tree';
import LayerMenu from './LayerMenu.vue';
export const useClick = (
services: Services | undefined,
isCtrlKeyDown: Ref<boolean>,
nodeStatusMap: ComputedRef<Map<Id, LayerNodeStatus> | undefined>,
) => {
// 触发画布选中
const select = async (data: MNode) => {
if (!data.id) {
throw new Error('没有id');
}
if (isCtrlKeyDown.value) {
multiSelect(data);
} else {
await services?.editorService.select(data);
services?.editorService.get('stage')?.select(data.id);
}
};
const multiSelect = async (data: MNode) => {
const nodes = services?.editorService.get('nodes') || [];
const newNodes: Id[] = [];
let isCancel = false;
nodes.forEach((node) => {
if (node.id === data.id) {
isCancel = true;
return;
}
newNodes.push(node.id);
});
// 只剩一个不能取消选中
if (!isCancel || newNodes.length === 0) {
newNodes.push(data.id);
}
await services?.editorService.multiSelect(newNodes);
services?.editorService.get('stage')?.multiSelect(newNodes);
};
const throttleTime = 300;
// 鼠标在组件树移动触发高亮
const highlightHandler = throttle((event: MouseEvent, data: TreeNodeData) => {
highlight(data);
}, throttleTime);
// 触发画布高亮
const highlight = (data: TreeNodeData) => {
services?.editorService?.highlight(data);
services?.editorService?.get('stage')?.highlight(data.id);
};
const nodeClickHandler = (event: MouseEvent, data: TreeNodeData) => {
if (!nodeStatusMap?.value) return;
if (services?.uiService.get('uiSelectMode')) {
document.dispatchEvent(new CustomEvent(UI_SELECT_MODE_EVENT_NAME, { detail: data }));
return;
}
if (data.items && data.items.length > 0 && !isCtrlKeyDown.value) {
updateStatus(nodeStatusMap.value, data.id, {
expand: true,
});
}
nextTick(() => {
select(data);
});
};
// 右键菜单
const menu = ref<InstanceType<typeof LayerMenu>>();
return {
menu,
nodeClickHandler,
nodeContentmenuHandler(event: MouseEvent, data: TreeNodeData) {
event.preventDefault();
const nodes = services?.editorService.get('nodes') || [];
if (nodes.length < 2 || !nodes.includes(data)) {
nodeClickHandler(event, data);
}
menu.value?.show(event);
},
highlightHandler,
};
};

View File

@ -1,21 +1,10 @@
import { type ComputedRef, ref } from 'vue';
import { Id, MNode, MPage } from '@tmagic/schema';
import { getKeys } from '@tmagic/utils';
import { LayerNodeStatus } from '@editor/type';
import { traverseNode } from '@editor/utils';
export const updateStatus = (nodeStatusMap: Map<Id, LayerNodeStatus>, id: Id, status: Partial<LayerNodeStatus>) => {
const nodeStatus = nodeStatusMap.get(id);
if (!nodeStatus) return;
getKeys(status).forEach((key) => {
if (nodeStatus[key] !== undefined && status[key] !== undefined) {
nodeStatus[key] = Boolean(status[key]);
}
});
};
import { updateStatus } from '@editor/utils/tree';
export const useFilter = (
nodeStatusMap: ComputedRef<Map<Id, LayerNodeStatus> | undefined>,

View File

@ -1,9 +1,13 @@
import { type Ref, ref, watchEffect } from 'vue';
import Tree from '@editor/components/Tree.vue';
import type { Services } from '@editor/type';
import { KeyBindingContainerKey } from '@editor/utils/keybinding-config';
export const useKeybinding = (services: Services | undefined, contianer: Ref<HTMLDivElement | undefined>) => {
export const useKeybinding = (
services: Services | undefined,
contianer: Ref<InstanceType<typeof Tree> | undefined>,
) => {
const keybindingService = services?.keybindingService;
// 是否多选
@ -37,7 +41,7 @@ export const useKeybinding = (services: Services | undefined, contianer: Ref<HTM
watchEffect(() => {
if (contianer.value) {
globalThis.addEventListener('blur', windowBlurHandler);
keybindingService?.registeEl(KeyBindingContainerKey.LAYER_PANEL, contianer.value);
keybindingService?.registeEl(KeyBindingContainerKey.LAYER_PANEL, contianer.value.$el);
} else {
globalThis.removeEventListener('blur', windowBlurHandler);
keybindingService?.unregisteEl(KeyBindingContainerKey.LAYER_PANEL);

View File

@ -5,8 +5,7 @@ import { getNodePath } from '@tmagic/utils';
import { LayerNodeStatus, Services } from '@editor/type';
import { traverseNode } from '@editor/utils';
import { updateStatus } from './use-filter';
import { updateStatus } from '@editor/utils/tree';
const createPageNodeStatus = (services: Services | undefined, pageId: Id) => {
const map = new Map<Id, LayerNodeStatus>();
@ -15,6 +14,7 @@ const createPageNodeStatus = (services: Services | undefined, pageId: Id) => {
visible: true,
expand: true,
selected: true,
draggable: false,
});
services?.editorService.getNodeById(pageId)?.items.forEach((node: MNode) =>
@ -23,6 +23,7 @@ const createPageNodeStatus = (services: Services | undefined, pageId: Id) => {
visible: true,
expand: false,
selected: false,
draggable: true,
});
}),
);
@ -87,6 +88,7 @@ export const useNodeStatus = (services: Services | undefined, page: ComputedRef<
visible: true,
expand: Array.isArray(node.items),
selected: true,
draggable: true,
});
});
});

View File

@ -1,79 +1,7 @@
.magic-editor-layer-panel {
$--node-height: 22px;
.m-editor-layer-panel {
background: $--sidebar-content-background-color;
.magic-editor-layer-tree {
.m-editor-tree {
padding-top: 48px;
background-color: $--sidebar-content-background-color;
color: $--font-color;
font-size: 13px;
.magic-editor-layer-node {
cursor: pointer;
white-space: nowrap;
.layer-node {
display: flex;
align-items: center;
&:hover {
background-color: $--hover-color;
color: $--font-color;
}
&.selected {
background-color: $--theme-color;
color: $--hover-color;
}
&.drag-inner {
.layer-node-content {
background-color: rgba($color: $--theme-color, $alpha: 0.5);
color: $--hover-color;
}
}
&.drag-before {
.layer-node-content {
border-top-color: rgba($color: $--theme-color, $alpha: 0.5);
}
}
&.drag-after {
.layer-node-content {
border-bottom-color: rgba($color: $--theme-color, $alpha: 0.5);
}
}
.expand-icon {
padding: 4px;
box-sizing: content-box;
font-size: 14px;
}
.layer-node-content {
display: flex;
flex: 1;
justify-content: space-between;
height: $--node-height;
border-top: 2px solid transparent;
border-bottom: 2px solid transparent;
.layer-node-label {
line-height: $--node-height;
flex: 1;
width: 100px;
overflow: hidden;
text-overflow: ellipsis;
}
}
.layer-node-tool {
margin-right: 15px;
display: flex;
align-items: center;
}
}
}
}
}

View File

@ -22,3 +22,4 @@
@import "./data-source-methods.scss";
@import "./data-source-input.scss";
@import "./key-value.scss";
@import "./tree.scss";

View File

@ -0,0 +1,72 @@
.m-editor-tree {
$--node-height: 22px;
color: $--font-color;
font-size: 13px;
.m-editor-tree-node {
cursor: pointer;
white-space: nowrap;
.tree-node {
display: flex;
align-items: center;
&:hover {
background-color: $--hover-color;
color: $--font-color;
}
&.selected {
background-color: $--theme-color;
color: $--hover-color;
}
&.drag-inner {
.tree-node-content {
background-color: rgba($color: $--theme-color, $alpha: 0.5);
color: $--hover-color;
}
}
&.drag-before {
.tree-node-content {
border-top-color: rgba($color: $--theme-color, $alpha: 0.5);
}
}
&.drag-after {
.tree-node-content {
border-bottom-color: rgba($color: $--theme-color, $alpha: 0.5);
}
}
.expand-icon {
padding: 4px;
box-sizing: content-box;
font-size: 14px;
}
.tree-node-content {
display: flex;
flex: 1;
justify-content: space-between;
height: $--node-height;
border-top: 2px solid transparent;
border-bottom: 2px solid transparent;
.tree-node-label {
line-height: $--node-height;
flex: 1;
width: 100px;
overflow: hidden;
text-overflow: ellipsis;
}
}
.tree-node-tool {
margin-right: 15px;
display: flex;
align-items: center;
}
}
}
}

View File

@ -595,6 +595,8 @@ export interface LayerNodeStatus {
expand: boolean;
/** 选中 */
selected: boolean;
/** 是否可拖拽 */
draggable: boolean;
}
/** 拖拽类型 */
@ -607,3 +609,9 @@ export enum DragType {
/** 当uiService.get('uiSelectMode')为true,点击组件(包括任何形式,组件树/画布)时触发的事件名 */
export const UI_SELECT_MODE_EVENT_NAME = 'ui-select';
export interface TreeNodeData {
id: Id;
name?: string;
items?: TreeNodeData[];
}

View File

@ -0,0 +1,15 @@
import type { Id } from '@tmagic/schema';
import { getKeys } from '@tmagic/utils';
import type { LayerNodeStatus } from '@editor/type';
export const updateStatus = (nodeStatusMap: Map<Id, LayerNodeStatus>, id: Id, status: Partial<LayerNodeStatus>) => {
const nodeStatus = nodeStatusMap.get(id);
if (!nodeStatus) return;
getKeys(status).forEach((key) => {
if (nodeStatus[key] !== undefined && status[key] !== undefined) {
nodeStatus[key] = Boolean(status[key]);
}
});
};