@tmagic/editor 1.3.5 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5432 -0
- package/dist/style.css +88 -62
- package/dist/tmagic-editor.js +2448 -2016
- package/dist/tmagic-editor.js.map +1 -1
- package/dist/tmagic-editor.umd.cjs +2453 -2019
- package/dist/tmagic-editor.umd.cjs.map +1 -1
- package/package.json +23 -23
- package/src/Editor.vue +15 -7
- package/src/components/CodeBlockEditor.vue +2 -2
- package/src/components/ContentMenu.vue +5 -5
- package/src/components/FloatingBox.vue +117 -0
- package/src/components/ScrollBar.vue +2 -2
- package/src/components/ScrollViewer.vue +2 -2
- package/src/components/SplitView.vue +2 -2
- package/src/components/Tree.vue +5 -0
- package/src/components/TreeNode.vue +10 -4
- package/src/editorProps.ts +20 -3
- package/src/fields/PageFragmentSelect.vue +55 -0
- package/src/hooks/use-filter.ts +55 -0
- package/src/hooks/use-getso.ts +2 -2
- package/src/hooks/use-node-status.ts +48 -0
- package/src/hooks/use-stage.ts +19 -1
- package/src/index.ts +3 -0
- package/src/initService.ts +43 -17
- package/src/layouts/AddPageBox.vue +16 -4
- package/src/layouts/CodeEditor.vue +2 -2
- package/src/layouts/Framework.vue +17 -3
- package/src/layouts/PropsPanel.vue +2 -2
- package/src/layouts/page-bar/AddButton.vue +48 -0
- package/src/layouts/page-bar/PageBar.vue +158 -0
- package/src/layouts/{workspace → page-bar}/PageBarScrollContainer.vue +61 -35
- package/src/layouts/page-bar/SwitchTypeButton.vue +45 -0
- package/src/layouts/sidebar/Sidebar.vue +9 -2
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +55 -65
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +2 -1
- package/src/layouts/sidebar/data-source/DataSourceList.vue +63 -59
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
- package/src/layouts/sidebar/layer/LayerMenu.vue +39 -27
- package/src/layouts/sidebar/layer/LayerPanel.vue +31 -8
- package/src/layouts/sidebar/layer/use-click.ts +5 -3
- package/src/layouts/sidebar/layer/use-keybinding.ts +8 -8
- package/src/layouts/sidebar/layer/use-node-status.ts +10 -13
- package/src/layouts/workspace/Workspace.vue +6 -7
- package/src/layouts/workspace/viewer/NodeListMenu.vue +68 -129
- package/src/layouts/workspace/viewer/Stage.vue +14 -6
- package/src/layouts/workspace/viewer/ViewerMenu.vue +94 -86
- package/src/services/BaseService.ts +63 -26
- package/src/services/codeBlock.ts +7 -1
- package/src/services/dep.ts +12 -8
- package/src/services/editor.ts +131 -64
- package/src/services/history.ts +6 -2
- package/src/services/keybinding.ts +48 -13
- package/src/services/props.ts +70 -12
- package/src/services/storage.ts +23 -13
- package/src/services/ui.ts +4 -1
- package/src/theme/code-block.scss +0 -16
- package/src/theme/floating-box.scss +30 -0
- package/src/theme/layer-panel.scss +19 -0
- package/src/theme/page-bar.scss +18 -1
- package/src/theme/stage.scss +24 -0
- package/src/theme/theme.scss +1 -1
- package/src/theme/tree.scss +12 -1
- package/src/type.ts +28 -10
- package/src/utils/compose.ts +25 -6
- package/src/utils/content-menu.ts +6 -7
- package/src/utils/editor.ts +34 -18
- package/src/utils/operator.ts +5 -6
- package/types/components/CodeBlockEditor.vue.d.ts +6 -2
- package/types/components/CodeParams.vue.d.ts +3 -1
- package/types/components/ContentMenu.vue.d.ts +13 -5
- package/types/components/FloatingBox.vue.d.ts +78 -0
- package/types/components/Resizer.vue.d.ts +6 -1
- package/types/components/ScrollBar.vue.d.ts +3 -1
- package/types/components/ScrollViewer.vue.d.ts +3 -3
- package/types/components/SearchInput.vue.d.ts +3 -1
- package/types/components/SplitView.vue.d.ts +8 -4
- package/types/components/ToolButton.vue.d.ts +3 -3
- package/types/components/Tree.vue.d.ts +27 -8
- package/types/components/TreeNode.vue.d.ts +24 -7
- package/types/editorProps.d.ts +16 -1
- package/types/fields/Code.vue.d.ts +6 -2
- package/types/fields/CodeLink.vue.d.ts +5 -3
- package/types/fields/CodeSelect.vue.d.ts +3 -1
- package/types/fields/CodeSelectCol.vue.d.ts +3 -1
- package/types/fields/DataSourceFieldSelect.vue.d.ts +3 -1
- package/types/fields/DataSourceFields.vue.d.ts +5 -3
- package/types/fields/DataSourceInput.vue.d.ts +8 -4
- package/types/fields/DataSourceMethodSelect.vue.d.ts +3 -1
- package/types/fields/DataSourceMethods.vue.d.ts +5 -3
- package/types/fields/DataSourceMocks.vue.d.ts +5 -3
- package/types/fields/DataSourceSelect.vue.d.ts +5 -3
- package/types/fields/EventSelect.vue.d.ts +3 -1
- package/types/fields/KeyValue.vue.d.ts +2 -2
- package/types/fields/PageFragmentSelect.vue.d.ts +31 -0
- package/types/fields/UISelect.vue.d.ts +3 -1
- package/types/hooks/use-code-block-edit.d.ts +14 -5
- package/types/hooks/use-data-source-method.d.ts +14 -5
- package/types/hooks/use-filter.d.ts +7 -0
- package/types/hooks/use-node-status.d.ts +6 -0
- package/types/index.d.ts +1 -0
- package/types/layouts/AddPageBox.vue.d.ts +14 -1
- package/types/layouts/CodeEditor.vue.d.ts +4 -1
- package/types/layouts/Framework.vue.d.ts +14 -1
- package/types/layouts/PropsPanel.vue.d.ts +19 -13
- package/types/layouts/{workspace/viewer/NodeListMenuTitle.vue.d.ts → page-bar/AddButton.vue.d.ts} +3 -2
- package/types/layouts/page-bar/PageBar.vue.d.ts +28 -0
- package/types/layouts/page-bar/PageBarScrollContainer.vue.d.ts +24 -0
- package/types/layouts/page-bar/SwitchTypeButton.vue.d.ts +20 -0
- package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +1 -1
- package/types/layouts/sidebar/Sidebar.vue.d.ts +7 -5
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +9 -3
- package/types/layouts/sidebar/code-block/CodeBlockListPanel.vue.d.ts +1 -1
- package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +3 -1
- package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +8 -2
- package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +1 -1
- package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +26 -3
- package/types/layouts/sidebar/layer/LayerPanel.vue.d.ts +3 -1
- package/types/layouts/sidebar/layer/use-click.d.ts +37 -6
- package/types/layouts/sidebar/layer/use-keybinding.d.ts +1 -1
- package/types/layouts/sidebar/layer/use-node-status.d.ts +3 -4
- package/types/layouts/workspace/Workspace.vue.d.ts +3 -1
- package/types/layouts/workspace/viewer/NodeListMenu.vue.d.ts +1 -14
- package/types/layouts/workspace/viewer/Stage.vue.d.ts +2 -0
- package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +9 -1
- package/types/services/BaseService.d.ts +4 -1
- package/types/services/dataSource.d.ts +1 -0
- package/types/services/dep.d.ts +6 -5
- package/types/services/editor.d.ts +12 -5
- package/types/services/history.d.ts +3 -2
- package/types/services/keybinding.d.ts +21 -1
- package/types/services/props.d.ts +25 -1
- package/types/services/storage.d.ts +7 -7
- package/types/type.d.ts +32 -17
- package/types/utils/compose.d.ts +1 -1
- package/types/utils/editor.d.ts +14 -8
- package/src/layouts/sidebar/layer/use-filter.ts +0 -59
- package/src/layouts/workspace/PageBar.vue +0 -87
- package/src/layouts/workspace/viewer/NodeListMenuTitle.vue +0 -68
- package/src/theme/dep-list.scss +0 -59
- package/types/layouts/sidebar/layer/use-filter.d.ts +0 -7
- package/types/layouts/workspace/PageBar.vue.d.ts +0 -15
- package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +0 -9
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { computed, inject, markRaw, ref } from 'vue';
|
|
7
7
|
import { Files, Plus } from '@element-plus/icons-vue';
|
|
8
8
|
|
|
9
|
-
import { isPage } from '@tmagic/utils';
|
|
9
|
+
import { isPage, isPageFragment } from '@tmagic/utils';
|
|
10
10
|
|
|
11
11
|
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
12
12
|
import FolderMinusIcon from '@editor/icons/FolderMinusIcon.vue';
|
|
@@ -17,9 +17,16 @@ defineOptions({
|
|
|
17
17
|
name: 'MEditorLayerMenu',
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
const props =
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const props = withDefaults(
|
|
21
|
+
defineProps<{
|
|
22
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
23
|
+
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
|
24
|
+
}>(),
|
|
25
|
+
{
|
|
26
|
+
layerContentMenu: () => [],
|
|
27
|
+
customContentMenu: (menus: (MenuButton | MenuComponent)[]) => menus,
|
|
28
|
+
},
|
|
29
|
+
);
|
|
23
30
|
|
|
24
31
|
const emit = defineEmits<{
|
|
25
32
|
'collapse-all': [];
|
|
@@ -82,29 +89,34 @@ const getSubMenuData = computed<MenuButton[]>(() => {
|
|
|
82
89
|
return [];
|
|
83
90
|
});
|
|
84
91
|
|
|
85
|
-
const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
92
|
+
const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
93
|
+
props.customContentMenu(
|
|
94
|
+
[
|
|
95
|
+
{
|
|
96
|
+
type: 'button',
|
|
97
|
+
text: '全部折叠',
|
|
98
|
+
icon: FolderMinusIcon,
|
|
99
|
+
display: () => isPage(node.value) || isPageFragment(node.value),
|
|
100
|
+
handler: () => {
|
|
101
|
+
emit('collapse-all');
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'button',
|
|
106
|
+
text: '新增',
|
|
107
|
+
icon: markRaw(Plus),
|
|
108
|
+
display: () => node.value?.items && nodes.value?.length === 1,
|
|
109
|
+
items: getSubMenuData.value,
|
|
110
|
+
},
|
|
111
|
+
useCopyMenu(),
|
|
112
|
+
usePasteMenu(),
|
|
113
|
+
useDeleteMenu(),
|
|
114
|
+
useMoveToMenu(services),
|
|
115
|
+
...props.layerContentMenu,
|
|
116
|
+
],
|
|
117
|
+
'layer',
|
|
118
|
+
),
|
|
119
|
+
);
|
|
108
120
|
|
|
109
121
|
const show = (e: MouseEvent) => {
|
|
110
122
|
menu.value?.show(e);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
v-if="page && nodeStatusMap"
|
|
9
9
|
tabindex="-1"
|
|
10
10
|
ref="tree"
|
|
11
|
-
:data="
|
|
11
|
+
:data="nodeData"
|
|
12
12
|
:node-status-map="nodeStatusMap"
|
|
13
13
|
@node-dragover="handleDragOver"
|
|
14
14
|
@node-dragstart="handleDragStart"
|
|
@@ -18,19 +18,28 @@
|
|
|
18
18
|
@node-mouseenter="mouseenterHandler"
|
|
19
19
|
@node-click="nodeClickHandler"
|
|
20
20
|
>
|
|
21
|
+
<template #tree-node-content="{ data: nodeData }">
|
|
22
|
+
<slot name="layer-node-content" :data="nodeData"> </slot>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
21
25
|
<template #tree-node-tool="{ data: nodeData }">
|
|
22
26
|
<slot name="layer-node-tool" :data="nodeData">
|
|
23
27
|
<LayerNodeTool :data="nodeData"></LayerNodeTool>
|
|
24
28
|
</slot>
|
|
25
29
|
</template>
|
|
26
30
|
|
|
27
|
-
<template #tree-node-
|
|
28
|
-
<slot name="layer-node-
|
|
31
|
+
<template #tree-node-label="{ data: nodeData }">
|
|
32
|
+
<slot name="layer-node-label" :data="nodeData"></slot>
|
|
29
33
|
</template>
|
|
30
34
|
</Tree>
|
|
31
35
|
|
|
32
36
|
<Teleport to="body">
|
|
33
|
-
<LayerMenu
|
|
37
|
+
<LayerMenu
|
|
38
|
+
ref="menu"
|
|
39
|
+
:layer-content-menu="layerContentMenu"
|
|
40
|
+
:custom-content-menu="customContentMenu"
|
|
41
|
+
@collapse-all="collapseAllHandler"
|
|
42
|
+
></LayerMenu>
|
|
34
43
|
</Teleport>
|
|
35
44
|
</TMagicScrollbar>
|
|
36
45
|
</template>
|
|
@@ -39,16 +48,17 @@
|
|
|
39
48
|
import { computed, inject, ref } from 'vue';
|
|
40
49
|
|
|
41
50
|
import { TMagicScrollbar } from '@tmagic/design';
|
|
51
|
+
import type { MNode } from '@tmagic/schema';
|
|
42
52
|
|
|
43
53
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
44
54
|
import Tree from '@editor/components/Tree.vue';
|
|
45
|
-
import {
|
|
55
|
+
import { useFilter } from '@editor/hooks/use-filter';
|
|
56
|
+
import { LayerPanelSlots, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
|
|
46
57
|
|
|
47
58
|
import LayerMenu from './LayerMenu.vue';
|
|
48
59
|
import LayerNodeTool from './LayerNodeTool.vue';
|
|
49
60
|
import { useClick } from './use-click';
|
|
50
61
|
import { useDrag } from './use-drag';
|
|
51
|
-
import { useFilter } from './use-filter';
|
|
52
62
|
import { useKeybinding } from './use-keybinding';
|
|
53
63
|
import { useNodeStatus } from './use-node-status';
|
|
54
64
|
|
|
@@ -60,6 +70,7 @@ defineOptions({
|
|
|
60
70
|
|
|
61
71
|
defineProps<{
|
|
62
72
|
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
73
|
+
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
|
63
74
|
}>();
|
|
64
75
|
|
|
65
76
|
const services = inject<Services>('services');
|
|
@@ -68,11 +79,23 @@ const editorService = services?.editorService;
|
|
|
68
79
|
const tree = ref<InstanceType<typeof Tree>>();
|
|
69
80
|
|
|
70
81
|
const page = computed(() => editorService?.get('page'));
|
|
82
|
+
const nodeData = computed<TreeNodeData[]>(() => (!page.value ? [] : [page.value]));
|
|
71
83
|
|
|
72
|
-
const { nodeStatusMap } = useNodeStatus(services
|
|
84
|
+
const { nodeStatusMap } = useNodeStatus(services);
|
|
73
85
|
const { isCtrlKeyDown } = useKeybinding(services, tree);
|
|
74
86
|
|
|
75
|
-
const
|
|
87
|
+
const filterNodeMethod = (v: string, data: MNode): boolean => {
|
|
88
|
+
let name = '';
|
|
89
|
+
if (data.name) {
|
|
90
|
+
name = data.name;
|
|
91
|
+
} else if (data.items) {
|
|
92
|
+
name = 'container';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return `${data.id}${name}${data.type}`.includes(v);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const { filterTextChangeHandler } = useFilter(nodeData, nodeStatusMap, filterNodeMethod);
|
|
76
99
|
|
|
77
100
|
const collapseAllHandler = () => {
|
|
78
101
|
if (!page.value || !nodeStatusMap.value) return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ComputedRef, nextTick, type Ref, ref } from 'vue';
|
|
1
|
+
import { computed, type ComputedRef, nextTick, type Ref, ref } from 'vue';
|
|
2
2
|
import { throttle } from 'lodash-es';
|
|
3
3
|
|
|
4
4
|
import { Id, MNode } from '@tmagic/schema';
|
|
@@ -13,13 +13,15 @@ export const useClick = (
|
|
|
13
13
|
isCtrlKeyDown: Ref<boolean>,
|
|
14
14
|
nodeStatusMap: ComputedRef<Map<Id, LayerNodeStatus> | undefined>,
|
|
15
15
|
) => {
|
|
16
|
+
const isMultiSelect = computed(() => isCtrlKeyDown.value && !services?.editorService.get('disabledMultiSelect'));
|
|
17
|
+
|
|
16
18
|
// 触发画布选中
|
|
17
19
|
const select = async (data: MNode) => {
|
|
18
20
|
if (!data.id) {
|
|
19
21
|
throw new Error('没有id');
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
if (
|
|
24
|
+
if (isMultiSelect.value) {
|
|
23
25
|
multiSelect(data);
|
|
24
26
|
} else {
|
|
25
27
|
await services?.editorService.select(data);
|
|
@@ -70,7 +72,7 @@ export const useClick = (
|
|
|
70
72
|
return;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
if (data.items && data.items.length > 0 && !
|
|
75
|
+
if (data.items && data.items.length > 0 && !isMultiSelect.value) {
|
|
74
76
|
updateStatus(nodeStatusMap.value, data.id, {
|
|
75
77
|
expand: true,
|
|
76
78
|
});
|
|
@@ -6,7 +6,7 @@ import { KeyBindingContainerKey } from '@editor/utils/keybinding-config';
|
|
|
6
6
|
|
|
7
7
|
export const useKeybinding = (
|
|
8
8
|
services: Services | undefined,
|
|
9
|
-
|
|
9
|
+
container: Ref<InstanceType<typeof Tree> | undefined>,
|
|
10
10
|
) => {
|
|
11
11
|
const keybindingService = services?.keybindingService;
|
|
12
12
|
|
|
@@ -17,17 +17,17 @@ export const useKeybinding = (
|
|
|
17
17
|
isCtrlKeyDown.value = false;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
keybindingService?.
|
|
20
|
+
keybindingService?.registerCommand('layer-panel-global-keyup', () => {
|
|
21
21
|
isCtrlKeyDown.value = false;
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
keybindingService?.
|
|
24
|
+
keybindingService?.registerCommand('layer-panel-global-keydown', () => {
|
|
25
25
|
isCtrlKeyDown.value = true;
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
keybindingService?.
|
|
28
|
+
keybindingService?.register([
|
|
29
29
|
{
|
|
30
|
-
command: 'layer-panel-global-
|
|
30
|
+
command: 'layer-panel-global-keydown',
|
|
31
31
|
keybinding: 'ctrl',
|
|
32
32
|
when: [['global', 'keydown']],
|
|
33
33
|
},
|
|
@@ -39,12 +39,12 @@ export const useKeybinding = (
|
|
|
39
39
|
]);
|
|
40
40
|
|
|
41
41
|
watchEffect(() => {
|
|
42
|
-
if (
|
|
42
|
+
if (container.value) {
|
|
43
43
|
globalThis.addEventListener('blur', windowBlurHandler);
|
|
44
|
-
keybindingService?.
|
|
44
|
+
keybindingService?.registerEl(KeyBindingContainerKey.LAYER_PANEL, container.value.$el);
|
|
45
45
|
} else {
|
|
46
46
|
globalThis.removeEventListener('blur', windowBlurHandler);
|
|
47
|
-
keybindingService?.
|
|
47
|
+
keybindingService?.unregisterEl(KeyBindingContainerKey.LAYER_PANEL);
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -1,31 +1,27 @@
|
|
|
1
|
-
import { computed,
|
|
1
|
+
import { computed, ref, watch } from 'vue';
|
|
2
2
|
|
|
3
|
-
import type { Id, MNode, MPage } from '@tmagic/schema';
|
|
3
|
+
import type { Id, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
|
4
4
|
import { getNodePath } from '@tmagic/utils';
|
|
5
5
|
|
|
6
6
|
import { LayerNodeStatus, Services } from '@editor/type';
|
|
7
7
|
import { traverseNode } from '@editor/utils';
|
|
8
8
|
import { updateStatus } from '@editor/utils/tree';
|
|
9
9
|
|
|
10
|
-
const createPageNodeStatus = (
|
|
11
|
-
services: Services | undefined,
|
|
12
|
-
pageId: Id,
|
|
13
|
-
initalLayerNodeStatus?: Map<Id, LayerNodeStatus>,
|
|
14
|
-
) => {
|
|
10
|
+
const createPageNodeStatus = (page: MPage | MPageFragment, initialLayerNodeStatus?: Map<Id, LayerNodeStatus>) => {
|
|
15
11
|
const map = new Map<Id, LayerNodeStatus>();
|
|
16
12
|
|
|
17
|
-
map.set(
|
|
13
|
+
map.set(page.id, {
|
|
18
14
|
visible: true,
|
|
19
15
|
expand: true,
|
|
20
16
|
selected: true,
|
|
21
17
|
draggable: false,
|
|
22
18
|
});
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
traverseNode(node, (node) => {
|
|
20
|
+
page.items.forEach((node: MNode) =>
|
|
21
|
+
traverseNode<MNode>(node, (node) => {
|
|
26
22
|
map.set(
|
|
27
23
|
node.id,
|
|
28
|
-
|
|
24
|
+
initialLayerNodeStatus?.get(node.id) || {
|
|
29
25
|
visible: true,
|
|
30
26
|
expand: false,
|
|
31
27
|
selected: false,
|
|
@@ -38,7 +34,8 @@ const createPageNodeStatus = (
|
|
|
38
34
|
return map;
|
|
39
35
|
};
|
|
40
36
|
|
|
41
|
-
export const useNodeStatus = (services: Services | undefined
|
|
37
|
+
export const useNodeStatus = (services: Services | undefined) => {
|
|
38
|
+
const page = computed(() => services?.editorService.get('page'));
|
|
42
39
|
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
|
43
40
|
|
|
44
41
|
/** 所有页面的节点状态 */
|
|
@@ -57,7 +54,7 @@ export const useNodeStatus = (services: Services | undefined, page: ComputedRef<
|
|
|
57
54
|
return;
|
|
58
55
|
}
|
|
59
56
|
// 生成节点状态
|
|
60
|
-
nodeStatusMaps.value.set(page.id, createPageNodeStatus(
|
|
57
|
+
nodeStatusMaps.value.set(page.id, createPageNodeStatus(page, nodeStatusMaps.value.get(page.id)));
|
|
61
58
|
},
|
|
62
59
|
{
|
|
63
60
|
immediate: true,
|
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
<Breadcrumb></Breadcrumb>
|
|
4
4
|
|
|
5
5
|
<slot name="stage">
|
|
6
|
-
<MagicStage
|
|
6
|
+
<MagicStage
|
|
7
|
+
v-if="page"
|
|
8
|
+
:stage-content-menu="stageContentMenu"
|
|
9
|
+
:custom-content-menu="customContentMenu"
|
|
10
|
+
></MagicStage>
|
|
7
11
|
</slot>
|
|
8
12
|
|
|
9
13
|
<slot name="workspace-content"></slot>
|
|
10
|
-
|
|
11
|
-
<PageBar>
|
|
12
|
-
<template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
|
|
13
|
-
<template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
|
|
14
|
-
</PageBar>
|
|
15
14
|
</div>
|
|
16
15
|
</template>
|
|
17
16
|
|
|
@@ -22,7 +21,6 @@ import type { MenuButton, MenuComponent, Services, WorkspaceSlots } from '@edito
|
|
|
22
21
|
|
|
23
22
|
import MagicStage from './viewer/Stage.vue';
|
|
24
23
|
import Breadcrumb from './Breadcrumb.vue';
|
|
25
|
-
import PageBar from './PageBar.vue';
|
|
26
24
|
|
|
27
25
|
defineSlots<WorkspaceSlots>();
|
|
28
26
|
|
|
@@ -32,6 +30,7 @@ defineOptions({
|
|
|
32
30
|
|
|
33
31
|
defineProps<{
|
|
34
32
|
stageContentMenu: (MenuButton | MenuComponent)[];
|
|
33
|
+
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
|
35
34
|
}>();
|
|
36
35
|
|
|
37
36
|
const services = inject<Services>('services');
|
|
@@ -1,175 +1,114 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
ref="
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
:
|
|
9
|
-
|
|
2
|
+
<TMagicTooltip v-if="page && buttonVisible" content="点击查看当前位置下的组件">
|
|
3
|
+
<div ref="button" class="m-editor-stage-float-button" @click="visible = true">可选组件</div>
|
|
4
|
+
</TMagicTooltip>
|
|
5
|
+
<FloatingBox
|
|
6
|
+
v-if="page && nodeStatusMap && buttonVisible"
|
|
7
|
+
ref="box"
|
|
8
|
+
v-model:visible="visible"
|
|
9
|
+
title="当前位置下的组件"
|
|
10
|
+
:position="menuPosition"
|
|
10
11
|
>
|
|
11
|
-
<template #
|
|
12
|
-
<
|
|
12
|
+
<template #body>
|
|
13
|
+
<Tree
|
|
14
|
+
class="m-editor-node-list-menu magic-editor-layer-tree"
|
|
15
|
+
:data="nodeData"
|
|
16
|
+
:node-status-map="nodeStatusMap"
|
|
17
|
+
@node-click="clickHandler"
|
|
18
|
+
></Tree>
|
|
13
19
|
</template>
|
|
14
|
-
</
|
|
20
|
+
</FloatingBox>
|
|
15
21
|
</template>
|
|
16
22
|
|
|
17
23
|
<script lang="ts" setup>
|
|
18
|
-
import {
|
|
19
|
-
import type { OnDrag } from 'gesto';
|
|
24
|
+
import { computed, inject, nextTick, ref, watch } from 'vue';
|
|
20
25
|
|
|
26
|
+
import { TMagicTooltip } from '@tmagic/design';
|
|
21
27
|
import type { MNode } from '@tmagic/schema';
|
|
22
|
-
import { StageDragStatus } from '@tmagic/stage';
|
|
23
|
-
import { getNodes } from '@tmagic/utils';
|
|
24
28
|
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
|
|
28
|
-
import
|
|
29
|
-
|
|
30
|
-
const PINNED_STATUE_CACHE_KEY = 'tmagic-pinned-node-list-pinned-status';
|
|
31
|
-
|
|
32
|
-
const props = defineProps<{ isMultiSelect?: boolean }>();
|
|
33
|
-
|
|
34
|
-
const menu = ref<InstanceType<typeof ContentMenu>>();
|
|
35
|
-
const nodeList = ref<MNode[]>([]);
|
|
36
|
-
const pinned = ref(Boolean(globalThis.localStorage.getItem(PINNED_STATUE_CACHE_KEY)));
|
|
37
|
-
const firstShow = ref(true);
|
|
29
|
+
import FloatingBox from '@editor/components/FloatingBox.vue';
|
|
30
|
+
import Tree from '@editor/components/Tree.vue';
|
|
31
|
+
import { useFilter } from '@editor/hooks/use-filter';
|
|
32
|
+
import { useNodeStatus } from '@editor/layouts/sidebar/layer/use-node-status';
|
|
33
|
+
import type { Services, TreeNodeData } from '@editor/type';
|
|
38
34
|
|
|
39
35
|
const services = inject<Services>('services');
|
|
40
36
|
const editorService = services?.editorService;
|
|
41
|
-
|
|
37
|
+
|
|
38
|
+
const visible = ref(false);
|
|
39
|
+
const buttonVisible = ref(false);
|
|
40
|
+
const button = ref<HTMLDivElement>();
|
|
41
|
+
const box = ref<InstanceType<typeof FloatingBox>>();
|
|
42
42
|
|
|
43
43
|
const stage = computed(() => editorService?.get('stage'));
|
|
44
44
|
const page = computed(() => editorService?.get('page'));
|
|
45
|
-
const
|
|
45
|
+
const nodes = computed(() => editorService?.get('nodes') || []);
|
|
46
|
+
const nodeData = computed<TreeNodeData[]>(() => (!page.value ? [] : [page.value]));
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
const { nodeStatusMap } = useNodeStatus(services);
|
|
48
49
|
|
|
49
|
-
const
|
|
50
|
-
if (timeout) {
|
|
51
|
-
globalThis.clearTimeout(timeout);
|
|
52
|
-
}
|
|
50
|
+
const filterNodeMethod = (value: string, data: MNode): boolean => data.id === value;
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
nodeList.value = [];
|
|
59
|
-
menu.value?.hide();
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const clearTimeoutLazy = () => {
|
|
63
|
-
globalThis.setTimeout(() => {
|
|
64
|
-
if (timeout) {
|
|
65
|
-
globalThis.clearTimeout(timeout);
|
|
66
|
-
}
|
|
67
|
-
}, 300);
|
|
68
|
-
};
|
|
52
|
+
const { filterTextChangeHandler } = useFilter(nodeData, nodeStatusMap, filterNodeMethod);
|
|
69
53
|
|
|
70
54
|
const unWatch = watch(
|
|
71
55
|
stage,
|
|
72
56
|
(stage) => {
|
|
73
57
|
if (!stage) return;
|
|
74
58
|
|
|
75
|
-
|
|
76
|
-
cancel();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
stage.on('mousemove', (event: MouseEvent) => {
|
|
80
|
-
cancel();
|
|
81
|
-
|
|
82
|
-
if (props.isMultiSelect || stage.getDragStatus() !== StageDragStatus.END) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
timeout = globalThis.setTimeout(() => {
|
|
87
|
-
const els = stage.renderer.getElementsFromPoint(event) || [];
|
|
59
|
+
nextTick(() => unWatch());
|
|
88
60
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
61
|
+
stage.on('select', (el: HTMLElement, event: MouseEvent) => {
|
|
62
|
+
const els = stage.renderer.getElementsFromPoint(event) || [];
|
|
63
|
+
const ids = els.map((el) => el.id).filter((id) => Boolean(id));
|
|
93
64
|
|
|
94
|
-
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
65
|
+
buttonVisible.value = ids.length > 3;
|
|
97
66
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (nodeList.value.length > 1) {
|
|
101
|
-
menu.value?.show(pinned.value && !firstShow.value ? undefined : event);
|
|
102
|
-
firstShow.value = false;
|
|
103
|
-
}
|
|
104
|
-
}, 1500);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
stage.on('mouseleave', () => {
|
|
108
|
-
// mouseleave后,大概率还有最后一个mousemove事件,这里延迟清除
|
|
109
|
-
clearTimeoutLazy();
|
|
67
|
+
filterTextChangeHandler(ids);
|
|
110
68
|
});
|
|
111
|
-
|
|
112
|
-
unWatch();
|
|
113
69
|
},
|
|
114
70
|
{
|
|
115
71
|
immediate: true,
|
|
116
72
|
},
|
|
117
73
|
);
|
|
118
74
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
map[item.type] = item;
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
return map;
|
|
127
|
-
});
|
|
75
|
+
watch(
|
|
76
|
+
nodes,
|
|
77
|
+
(nodes) => {
|
|
78
|
+
if (!nodeStatusMap.value) return;
|
|
128
79
|
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
let text = node.name;
|
|
132
|
-
let icon: string | Component<{}, {}, any> | undefined;
|
|
133
|
-
if (node.type) {
|
|
134
|
-
const item = componentMap.value[node.type];
|
|
135
|
-
text += ` (${item?.text})`;
|
|
136
|
-
icon = item?.icon;
|
|
80
|
+
for (const [id, status] of nodeStatusMap.value.entries()) {
|
|
81
|
+
status.selected = nodes.some((node) => node.id === id);
|
|
137
82
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
id: node.id,
|
|
143
|
-
icon,
|
|
144
|
-
handler: async () => {
|
|
145
|
-
await editorService?.select(node);
|
|
146
|
-
stage.value?.select(node.id);
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
}),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
immediate: true,
|
|
86
|
+
},
|
|
150
87
|
);
|
|
151
88
|
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
89
|
+
const clickHandler = async (event: MouseEvent, data: TreeNodeData) => {
|
|
90
|
+
await editorService?.select(data.id);
|
|
91
|
+
stage.value?.select(data.id);
|
|
155
92
|
};
|
|
156
93
|
|
|
157
|
-
const
|
|
158
|
-
|
|
94
|
+
const menuPosition = ref({
|
|
95
|
+
left: 0,
|
|
96
|
+
top: 0,
|
|
97
|
+
});
|
|
159
98
|
|
|
160
|
-
|
|
99
|
+
watch(visible, async (visible) => {
|
|
100
|
+
if (!button.value || !visible) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
161
103
|
|
|
162
|
-
|
|
163
|
-
clientY: menuPosition.top + deltaY,
|
|
164
|
-
clientX: menuPosition.left + deltaX,
|
|
165
|
-
});
|
|
166
|
-
};
|
|
104
|
+
await nextTick();
|
|
167
105
|
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
};
|
|
106
|
+
const rect = button.value.getBoundingClientRect();
|
|
107
|
+
const height = box.value?.target?.clientHeight || 0;
|
|
171
108
|
|
|
172
|
-
|
|
173
|
-
|
|
109
|
+
menuPosition.value = {
|
|
110
|
+
left: rect.left + rect.width + 5,
|
|
111
|
+
top: rect.top - height / 2 + rect.height / 2,
|
|
112
|
+
};
|
|
174
113
|
});
|
|
175
114
|
</script>
|
|
@@ -22,15 +22,21 @@
|
|
|
22
22
|
@drop="dropHandler"
|
|
23
23
|
@dragover="dragoverHandler"
|
|
24
24
|
></div>
|
|
25
|
+
<NodeListMenu></NodeListMenu>
|
|
26
|
+
|
|
25
27
|
<Teleport to="body">
|
|
26
|
-
<ViewerMenu
|
|
27
|
-
|
|
28
|
+
<ViewerMenu
|
|
29
|
+
ref="menu"
|
|
30
|
+
:is-multi-select="isMultiSelect"
|
|
31
|
+
:stage-content-menu="stageContentMenu"
|
|
32
|
+
:custom-content-menu="customContentMenu"
|
|
33
|
+
></ViewerMenu>
|
|
28
34
|
</Teleport>
|
|
29
35
|
</ScrollViewer>
|
|
30
36
|
</template>
|
|
31
37
|
|
|
32
38
|
<script lang="ts" setup>
|
|
33
|
-
import { computed, inject, markRaw, nextTick,
|
|
39
|
+
import { computed, inject, markRaw, nextTick, onBeforeUnmount, onMounted, ref, toRaw, watch, watchEffect } from 'vue';
|
|
34
40
|
import { cloneDeep } from 'lodash-es';
|
|
35
41
|
|
|
36
42
|
import type { MApp, MContainer } from '@tmagic/schema';
|
|
@@ -51,6 +57,7 @@ defineOptions({
|
|
|
51
57
|
|
|
52
58
|
defineProps<{
|
|
53
59
|
stageContentMenu: (MenuButton | MenuComponent)[];
|
|
60
|
+
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
|
54
61
|
}>();
|
|
55
62
|
|
|
56
63
|
let stage: StageCore | null = null;
|
|
@@ -107,6 +114,7 @@ watch(zoom, (zoom) => {
|
|
|
107
114
|
|
|
108
115
|
watch(page, (page) => {
|
|
109
116
|
if (runtime && page) {
|
|
117
|
+
services?.editorService.set('stageLoading', true);
|
|
110
118
|
runtime.updatePageId?.(page.id);
|
|
111
119
|
nextTick(() => {
|
|
112
120
|
stage?.select(page.id);
|
|
@@ -134,15 +142,15 @@ const resizeObserver = new ResizeObserver((entries) => {
|
|
|
134
142
|
onMounted(() => {
|
|
135
143
|
if (stageWrap.value?.container) {
|
|
136
144
|
resizeObserver.observe(stageWrap.value.container);
|
|
137
|
-
services?.keybindingService.
|
|
145
|
+
services?.keybindingService.registerEl(KeyBindingContainerKey.STAGE, stageWrap.value.container);
|
|
138
146
|
}
|
|
139
147
|
});
|
|
140
148
|
|
|
141
|
-
|
|
149
|
+
onBeforeUnmount(() => {
|
|
142
150
|
stage?.destroy();
|
|
143
151
|
resizeObserver.disconnect();
|
|
144
152
|
services?.editorService.set('stage', null);
|
|
145
|
-
services?.keybindingService.
|
|
153
|
+
services?.keybindingService.unregisterEl('stage');
|
|
146
154
|
services?.editorService.off('root-change', rootChangeHandler);
|
|
147
155
|
});
|
|
148
156
|
|