Skip to content

Commit

Permalink
feat(editor,stage): 优化可选组件交互
Browse files Browse the repository at this point in the history
  • Loading branch information
roymondchen committed Dec 7, 2023
1 parent 5c6a345 commit 258d2bc
Show file tree
Hide file tree
Showing 15 changed files with 306 additions and 256 deletions.
117 changes: 117 additions & 0 deletions packages/editor/src/components/FloatingBox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<template>
<Teleport to="body" v-if="visible">
<div ref="target" class="m-editor-float-box" :style="style">
<div ref="dragTarget" class="m-editor-float-box-title">
<slot name="title">
<span>{{ title }}</span>
</slot>
<div>
<TMagicButton text size="small" :icon="Close" @click="closeHandler"></TMagicButton>
</div>
</div>
<div class="m-editor-float-box-body">
<slot name="body"></slot>
</div>
</div>
</Teleport>
</template>

<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
import { Close } from '@element-plus/icons-vue';
import VanillaMoveable from 'moveable';
import { TMagicButton } from '@tmagic/design';
interface Position {
left: number;
top: number;
}
interface Rect {
width: number | string;
height: number | string;
}
const props = withDefaults(defineProps<{ visible: boolean; position?: Position; rect?: Rect; title?: string }>(), {
visible: false,
title: '',
position: () => ({ left: 0, top: 0 }),
rect: () => ({ width: 'auto', height: 'auto' }),
});
const emit = defineEmits<{
'update:visible': [boolean];
}>();
const target = ref<HTMLDivElement>();
const dragTarget = ref<HTMLDivElement>();
const style = computed(() => ({
left: `${props.position.left}px`,
top: `${props.position.top}px`,
width: typeof props.rect.width === 'string' ? props.rect.width : `${props.rect.width}px`,
height: typeof props.rect.height === 'string' ? props.rect.height : `${props.rect.height}px`,
}));
let moveable: VanillaMoveable | null = null;
const initMoveable = () => {
moveable = new VanillaMoveable(globalThis.document.body, {
className: 'm-editor-floating-box-moveable',
target: target.value,
draggable: true,
resizable: true,
edge: true,
keepRatio: false,
origin: false,
snappable: true,
dragTarget: dragTarget.value,
dragTargetSelf: false,
linePadding: 10,
controlPadding: 10,
});
moveable.on('drag', (e) => {
e.target.style.transform = e.transform;
});
moveable.on('resize', (e) => {
e.target.style.width = `${e.width}px`;
e.target.style.height = `${e.height}px`;
e.target.style.transform = e.drag.transform;
});
};
const destroyMoveable = () => {
moveable?.destroy();
moveable = null;
};
watch(
() => props.visible,
async (visible) => {
if (visible) {
await nextTick();
initMoveable();
} else {
destroyMoveable();
}
},
{
immediate: true,
},
);
onBeforeUnmount(() => {
destroyMoveable();
});
const closeHandler = () => {
emit('update:visible', false);
};
defineExpose({
target,
});
</script>
2 changes: 1 addition & 1 deletion packages/editor/src/components/TreeNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ 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 hasChilren = computed(() => props.data.items?.some((item) => props.nodeStatusMap.get(item.id)?.visible));
const handleDragStart = (event: DragEvent) => {
treeEmit?.('node-dragstart', event, props.data);
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/layouts/sidebar/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
<div
class="m-editor-sidebar-header-item"
v-for="(config, index) in sideBarItems"
v-show="!floatBoxStates?.get(config.$key)?.status"
draggable="true"
:key="config.$key ?? index"
:class="{ 'is-active': activeTabName === config.text }"
@click="activeTabName = config.text || `${index}`"
draggable="true"
@dragstart="dragstartHandler"
@dragend="dragendHandler(config.$key, $event)"
v-show="!floatBoxStates?.get(config.$key)?.status"
>
<MIcon v-if="config.icon" :icon="config.icon"></MIcon>
<div v-if="config.text" class="magic-editor-tab-panel-title">{{ config.text }}</div>
Expand Down
18 changes: 15 additions & 3 deletions packages/editor/src/layouts/sidebar/layer/LayerPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@
import { computed, inject, ref } from 'vue';
import { TMagicScrollbar } from '@tmagic/design';
import type { MNode } from '@tmagic/schema';
import SearchInput from '@editor/components/SearchInput.vue';
import Tree from '@editor/components/Tree.vue';
import { LayerPanelSlots, MenuButton, MenuComponent, Services } from '@editor/type';
import type { LayerPanelSlots, MenuButton, MenuComponent, Services } from '@editor/type';
import LayerMenu from './LayerMenu.vue';
import LayerNodeTool from './LayerNodeTool.vue';
Expand All @@ -69,10 +70,21 @@ const tree = ref<InstanceType<typeof Tree>>();
const page = computed(() => editorService?.get('page'));
const { nodeStatusMap } = useNodeStatus(services, page);
const { nodeStatusMap } = useNodeStatus(services);
const { isCtrlKeyDown } = useKeybinding(services, tree);
const { filterTextChangeHandler } = useFilter(nodeStatusMap, page);
const filterNodeMethod = (v: string, data: MNode): boolean => {
let name = '';
if (data.name) {
name = data.name;
} else if (data.items) {
name = 'container';
}
return `${data.id}${name}${data.type}`.includes(v);
};
const { filterTextChangeHandler } = useFilter(services, nodeStatusMap, filterNodeMethod);
const collapseAllHandler = () => {
if (!page.value || !nodeStatusMap.value) return;
Expand Down
68 changes: 35 additions & 33 deletions packages/editor/src/layouts/sidebar/layer/use-filter.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,60 @@
import { type ComputedRef, ref } from 'vue';
import { computed, type ComputedRef, ref } from 'vue';

import { Id, MNode, MPage } from '@tmagic/schema';
import { Id, MNode } from '@tmagic/schema';

import { LayerNodeStatus } from '@editor/type';
import { LayerNodeStatus, Services } from '@editor/type';
import { traverseNode } from '@editor/utils';
import { updateStatus } from '@editor/utils/tree';

export const useFilter = (
services: Services | undefined,
nodeStatusMap: ComputedRef<Map<Id, LayerNodeStatus> | undefined>,
page: ComputedRef<MPage | null | undefined>,
filterNodeMethod: (value: string, data: MNode) => boolean,
) => {
// tree方法:对树节点进行筛选时执行的方法
const filterIsMatch = (value: string, data: MNode): boolean => {
if (!value) {
return true;
}
let name = '';
if (data.name) {
name = data.name;
} else if (data.items) {
name = 'container';
}
return `${data.id}${name}${data.type}`.includes(value);
};
const page = computed(() => services?.editorService.get('page'));

const filterNode = (text: string) => (node: MNode, parents: MNode[]) => {
if (!nodeStatusMap.value) return;
// tree方法:对树节点进行筛选时执行的方法
const filterIsMatch = (value: string | string[], data: MNode): boolean => {
const string = !Array.isArray(value) ? [value] : value;

const visible = filterIsMatch(text, node);
if (visible && parents.length) {
parents.forEach((parent) => {
updateStatus(nodeStatusMap.value!, parent.id, {
visible,
expand: true,
});
});
if (!string.length) {
return true;
}

updateStatus(nodeStatusMap.value, node.id, {
visible,
});
return string.some((v) => filterNodeMethod(v, data));
};

const filter = (text: string) => {
const filter = (text: string | string[]) => {
if (!page.value?.items?.length) return;

page.value.items.forEach((node) => {
traverseNode(node, filterNode(text));
traverseNode(node, (node: MNode, parents: MNode[]) => {
if (!nodeStatusMap.value) return;

const visible = filterIsMatch(text, node);
if (visible && parents.length) {
console.log(
node.id,
parents.map((a) => a.id),
);
parents.forEach((parent) => {
updateStatus(nodeStatusMap.value!, parent.id, {
visible,
expand: true,
});
});
}

updateStatus(nodeStatusMap.value, node.id, {
visible,
});
});
});
};

return {
filterText: ref(''),
filterTextChangeHandler(text: string) {
filterTextChangeHandler(text: string | string[]) {
filter(text);
},
};
Expand Down
17 changes: 7 additions & 10 deletions packages/editor/src/layouts/sidebar/layer/use-node-status.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computed, type ComputedRef, ref, watch } from 'vue';
import { computed, ref, watch } from 'vue';

import type { Id, MNode, MPage } from '@tmagic/schema';
import { getNodePath } from '@tmagic/utils';
Expand All @@ -7,21 +7,17 @@ import { LayerNodeStatus, Services } from '@editor/type';
import { traverseNode } from '@editor/utils';
import { updateStatus } from '@editor/utils/tree';

const createPageNodeStatus = (
services: Services | undefined,
pageId: Id,
initalLayerNodeStatus?: Map<Id, LayerNodeStatus>,
) => {
const createPageNodeStatus = (page: MPage, initalLayerNodeStatus?: Map<Id, LayerNodeStatus>) => {
const map = new Map<Id, LayerNodeStatus>();

map.set(pageId, {
map.set(page.id, {
visible: true,
expand: true,
selected: true,
draggable: false,
});

services?.editorService.getNodeById(pageId)?.items.forEach((node: MNode) =>
page.items.forEach((node: MNode) =>
traverseNode(node, (node) => {
map.set(
node.id,
Expand All @@ -38,7 +34,8 @@ const createPageNodeStatus = (
return map;
};

export const useNodeStatus = (services: Services | undefined, page: ComputedRef<MPage | null | undefined>) => {
export const useNodeStatus = (services: Services | undefined) => {
const page = computed(() => services?.editorService.get('page'));
const nodes = computed(() => services?.editorService.get('nodes') || []);

/** 所有页面的节点状态 */
Expand All @@ -57,7 +54,7 @@ export const useNodeStatus = (services: Services | undefined, page: ComputedRef<
return;
}
// 生成节点状态
nodeStatusMaps.value.set(page.id, createPageNodeStatus(services, page.id, nodeStatusMaps.value.get(page.id)));
nodeStatusMaps.value.set(page.id, createPageNodeStatus(page, nodeStatusMaps.value.get(page.id)));
},
{
immediate: true,
Expand Down
Loading

0 comments on commit 258d2bc

Please sign in to comment.