@tmagic/editor 1.3.6 → 1.3.8
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 +34 -11
- package/dist/tmagic-editor.js +1360 -957
- package/dist/tmagic-editor.js.map +1 -1
- package/dist/tmagic-editor.umd.cjs +1382 -977
- package/dist/tmagic-editor.umd.cjs.map +1 -1
- package/package.json +17 -17
- package/src/Editor.vue +15 -7
- package/src/components/CodeBlockEditor.vue +2 -2
- package/src/components/ContentMenu.vue +5 -5
- 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 +1 -1
- package/src/components/TreeNode.vue +3 -3
- package/src/editorProps.ts +14 -4
- package/src/fields/PageFragmentSelect.vue +63 -0
- package/src/hooks/use-getso.ts +2 -2
- package/src/hooks/use-stage.ts +18 -1
- package/src/index.ts +3 -0
- package/src/initService.ts +15 -5
- package/src/layouts/AddPageBox.vue +16 -4
- package/src/layouts/CodeEditor.vue +2 -2
- package/src/layouts/Framework.vue +19 -3
- package/src/layouts/PropsPanel.vue +9 -7
- 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 +2 -0
- package/src/layouts/sidebar/layer/LayerMenu.vue +39 -27
- package/src/layouts/sidebar/layer/LayerPanel.vue +7 -1
- 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 +3 -3
- package/src/layouts/workspace/Workspace.vue +6 -7
- package/src/layouts/workspace/viewer/NodeListMenu.vue +2 -2
- package/src/layouts/workspace/viewer/Stage.vue +12 -5
- package/src/layouts/workspace/viewer/ViewerMenu.vue +93 -80
- package/src/services/dataSource.ts +17 -0
- package/src/services/dep.ts +4 -0
- package/src/services/editor.ts +66 -35
- package/src/services/history.ts +6 -2
- package/src/services/keybinding.ts +48 -13
- package/src/theme/code-block.scss +0 -16
- package/src/theme/page-bar.scss +18 -1
- package/src/theme/page-fragment-select.scss +14 -0
- package/src/theme/theme.scss +1 -0
- package/src/theme/tree.scss +6 -0
- package/src/type.ts +23 -7
- package/src/utils/content-menu.ts +2 -2
- package/src/utils/editor.ts +22 -15
- package/src/utils/operator.ts +4 -4
- package/types/components/ToolButton.vue.d.ts +2 -2
- package/types/editorProps.d.ts +11 -2
- package/types/fields/PageFragmentSelect.vue.d.ts +31 -0
- package/types/index.d.ts +1 -0
- package/types/layouts/AddPageBox.vue.d.ts +14 -1
- package/types/layouts/Framework.vue.d.ts +14 -1
- package/types/layouts/PropsPanel.vue.d.ts +19 -10
- package/types/layouts/page-bar/AddButton.vue.d.ts +16 -0
- 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/Sidebar.vue.d.ts +2 -0
- package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +22 -3
- package/types/layouts/sidebar/layer/LayerPanel.vue.d.ts +2 -0
- package/types/layouts/sidebar/layer/use-click.d.ts +25 -3
- package/types/layouts/sidebar/layer/use-keybinding.d.ts +1 -1
- package/types/layouts/workspace/Workspace.vue.d.ts +2 -0
- package/types/layouts/workspace/viewer/Stage.vue.d.ts +2 -0
- package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +8 -0
- package/types/services/dataSource.d.ts +2 -0
- package/types/services/dep.d.ts +1 -0
- package/types/services/editor.d.ts +3 -2
- package/types/services/history.d.ts +3 -2
- package/types/services/keybinding.d.ts +21 -1
- package/types/type.d.ts +26 -12
- package/types/utils/editor.d.ts +9 -7
- package/src/layouts/workspace/PageBar.vue +0 -87
- package/types/layouts/workspace/PageBar.vue.d.ts +0 -15
- package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +0 -9
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
:config="curFormConfig"
|
|
11
11
|
:extend-state="extendState"
|
|
12
12
|
@change="submit"
|
|
13
|
+
@error="errorHandler"
|
|
13
14
|
></MForm>
|
|
14
15
|
</div>
|
|
15
16
|
</template>
|
|
16
17
|
|
|
17
18
|
<script lang="ts" setup>
|
|
18
|
-
import { computed, getCurrentInstance, inject,
|
|
19
|
+
import { computed, getCurrentInstance, inject, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue';
|
|
19
20
|
|
|
20
|
-
import { tMagicMessage } from '@tmagic/design';
|
|
21
21
|
import type { FormState, FormValue } from '@tmagic/form';
|
|
22
22
|
import { MForm } from '@tmagic/form';
|
|
23
23
|
|
|
@@ -33,7 +33,7 @@ defineProps<{
|
|
|
33
33
|
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
34
34
|
}>();
|
|
35
35
|
|
|
36
|
-
const emit = defineEmits(['mounted']);
|
|
36
|
+
const emit = defineEmits(['mounted', 'submit-error', 'form-error']);
|
|
37
37
|
|
|
38
38
|
const internalInstance = getCurrentInstance();
|
|
39
39
|
const values = ref<FormValue>({});
|
|
@@ -64,7 +64,7 @@ onMounted(() => {
|
|
|
64
64
|
emit('mounted', internalInstance);
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
onBeforeUnmount(() => {
|
|
68
68
|
services?.propsService.off('props-configs-change', init);
|
|
69
69
|
});
|
|
70
70
|
|
|
@@ -79,11 +79,13 @@ const submit = async () => {
|
|
|
79
79
|
const values = await configForm.value?.submitForm();
|
|
80
80
|
services?.editorService.update(values);
|
|
81
81
|
} catch (e: any) {
|
|
82
|
-
|
|
83
|
-
tMagicMessage.closeAll();
|
|
84
|
-
tMagicMessage.error(e.message);
|
|
82
|
+
emit('submit-error', e);
|
|
85
83
|
}
|
|
86
84
|
};
|
|
87
85
|
|
|
86
|
+
const errorHandler = (e: any) => {
|
|
87
|
+
emit('form-error', e);
|
|
88
|
+
};
|
|
89
|
+
|
|
88
90
|
defineExpose({ configForm, submit });
|
|
89
91
|
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="showAddPageButton"
|
|
4
|
+
id="m-editor-page-bar-add-icon"
|
|
5
|
+
class="m-editor-page-bar-item m-editor-page-bar-item-icon"
|
|
6
|
+
@click="addPage"
|
|
7
|
+
>
|
|
8
|
+
<Icon :icon="Plus"></Icon>
|
|
9
|
+
</div>
|
|
10
|
+
<div v-else style="width: 21px"></div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { computed, inject, toRaw } from 'vue';
|
|
15
|
+
import { Plus } from '@element-plus/icons-vue';
|
|
16
|
+
|
|
17
|
+
import { NodeType } from '@tmagic/schema';
|
|
18
|
+
|
|
19
|
+
import Icon from '@editor/components/Icon.vue';
|
|
20
|
+
import type { Services } from '@editor/type';
|
|
21
|
+
import { generatePageNameByApp } from '@editor/utils/editor';
|
|
22
|
+
|
|
23
|
+
defineOptions({
|
|
24
|
+
name: 'MEditorPageBarAddButton',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const props = defineProps<{
|
|
28
|
+
type: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
const services = inject<Services>('services');
|
|
32
|
+
const uiService = services?.uiService;
|
|
33
|
+
const editorService = services?.editorService;
|
|
34
|
+
|
|
35
|
+
const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
|
|
36
|
+
|
|
37
|
+
const addPage = () => {
|
|
38
|
+
if (!editorService) return;
|
|
39
|
+
const root = toRaw(editorService.get('root'));
|
|
40
|
+
if (!root) throw new Error('root 不能为空');
|
|
41
|
+
const pageConfig = {
|
|
42
|
+
type: props.type,
|
|
43
|
+
name: generatePageNameByApp(root, props.type),
|
|
44
|
+
items: [],
|
|
45
|
+
};
|
|
46
|
+
editorService.add(pageConfig);
|
|
47
|
+
};
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-page-bar-tabs">
|
|
3
|
+
<SwitchTypeButton v-if="!disabledPageFragment" v-model="active" />
|
|
4
|
+
|
|
5
|
+
<PageBarScrollContainer :type="active">
|
|
6
|
+
<template #prepend>
|
|
7
|
+
<AddButton :type="active"></AddButton>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<div
|
|
11
|
+
v-for="item in list"
|
|
12
|
+
class="m-editor-page-bar-item"
|
|
13
|
+
:key="item.id"
|
|
14
|
+
:class="{ active: page?.id === item.id }"
|
|
15
|
+
@click="switchPage(item.id)"
|
|
16
|
+
>
|
|
17
|
+
<div class="m-editor-page-bar-title">
|
|
18
|
+
<slot name="page-bar-title" :page="item">
|
|
19
|
+
<span :title="item.name">{{ item.name || item.id }}</span>
|
|
20
|
+
</slot>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<TMagicPopover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
|
24
|
+
<div>
|
|
25
|
+
<slot name="page-bar-popover" :page="item">
|
|
26
|
+
<ToolButton
|
|
27
|
+
:data="{
|
|
28
|
+
type: 'button',
|
|
29
|
+
text: '复制',
|
|
30
|
+
icon: DocumentCopy,
|
|
31
|
+
handler: () => copy(item),
|
|
32
|
+
}"
|
|
33
|
+
></ToolButton>
|
|
34
|
+
<ToolButton
|
|
35
|
+
:data="{
|
|
36
|
+
type: 'button',
|
|
37
|
+
text: '删除',
|
|
38
|
+
icon: Delete,
|
|
39
|
+
handler: () => remove(item),
|
|
40
|
+
}"
|
|
41
|
+
></ToolButton>
|
|
42
|
+
</slot>
|
|
43
|
+
</div>
|
|
44
|
+
<template #reference>
|
|
45
|
+
<TMagicIcon class="m-editor-page-bar-menu-icon">
|
|
46
|
+
<CaretBottom></CaretBottom>
|
|
47
|
+
</TMagicIcon>
|
|
48
|
+
</template>
|
|
49
|
+
</TMagicPopover>
|
|
50
|
+
</div>
|
|
51
|
+
</PageBarScrollContainer>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script lang="ts" setup>
|
|
56
|
+
import { computed, inject, ref, watch } from 'vue';
|
|
57
|
+
import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
|
|
58
|
+
|
|
59
|
+
import { TMagicIcon, TMagicPopover } from '@tmagic/design';
|
|
60
|
+
import { Id, type MPage, type MPageFragment, NodeType } from '@tmagic/schema';
|
|
61
|
+
import { isPage, isPageFragment } from '@tmagic/utils';
|
|
62
|
+
|
|
63
|
+
import ToolButton from '@editor/components/ToolButton.vue';
|
|
64
|
+
import type { Services } from '@editor/type';
|
|
65
|
+
import { getPageFragmentList, getPageList } from '@editor/utils';
|
|
66
|
+
|
|
67
|
+
import AddButton from './AddButton.vue';
|
|
68
|
+
import PageBarScrollContainer from './PageBarScrollContainer.vue';
|
|
69
|
+
import SwitchTypeButton from './SwitchTypeButton.vue';
|
|
70
|
+
|
|
71
|
+
defineOptions({
|
|
72
|
+
name: 'MEditorPageBar',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
defineProps<{
|
|
76
|
+
disabledPageFragment: boolean;
|
|
77
|
+
}>();
|
|
78
|
+
|
|
79
|
+
const active = ref<NodeType.PAGE | NodeType.PAGE_FRAGMENT>(NodeType.PAGE);
|
|
80
|
+
|
|
81
|
+
const services = inject<Services>('services');
|
|
82
|
+
const editorService = services?.editorService;
|
|
83
|
+
|
|
84
|
+
const root = computed(() => editorService?.get('root'));
|
|
85
|
+
const page = computed(() => editorService?.get('page'));
|
|
86
|
+
const pageList = computed(() => getPageList(root.value));
|
|
87
|
+
const pageFragmentList = computed(() => getPageFragmentList(root.value));
|
|
88
|
+
|
|
89
|
+
const list = computed(() => (active.value === NodeType.PAGE ? pageList.value : pageFragmentList.value));
|
|
90
|
+
|
|
91
|
+
const activePage = ref<Id>('');
|
|
92
|
+
const activePageFragment = ref<Id>('');
|
|
93
|
+
|
|
94
|
+
watch(
|
|
95
|
+
page,
|
|
96
|
+
(page) => {
|
|
97
|
+
if (!page) {
|
|
98
|
+
if (active.value === NodeType.PAGE) {
|
|
99
|
+
activePage.value = '';
|
|
100
|
+
}
|
|
101
|
+
if (active.value === NodeType.PAGE_FRAGMENT) {
|
|
102
|
+
activePageFragment.value = '';
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (isPage(page)) {
|
|
108
|
+
activePage.value = page?.id;
|
|
109
|
+
if (active.value !== NodeType.PAGE) {
|
|
110
|
+
active.value = NodeType.PAGE;
|
|
111
|
+
}
|
|
112
|
+
} else if (isPageFragment(page)) {
|
|
113
|
+
activePageFragment.value = page?.id;
|
|
114
|
+
if (active.value !== NodeType.PAGE_FRAGMENT) {
|
|
115
|
+
active.value = NodeType.PAGE_FRAGMENT;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
immediate: true,
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
watch(active, (active) => {
|
|
125
|
+
if (active === NodeType.PAGE) {
|
|
126
|
+
if (!activePage.value) {
|
|
127
|
+
editorService?.selectRoot();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
switchPage(activePage.value);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (active === NodeType.PAGE_FRAGMENT) {
|
|
135
|
+
if (!activePageFragment.value) {
|
|
136
|
+
editorService?.selectRoot();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
switchPage(activePageFragment.value);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const switchPage = (id: Id) => {
|
|
144
|
+
editorService?.select(id);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const copy = (node: MPage | MPageFragment) => {
|
|
148
|
+
node && editorService?.copy(node);
|
|
149
|
+
editorService?.paste({
|
|
150
|
+
left: 0,
|
|
151
|
+
top: 0,
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const remove = (node: MPage | MPageFragment) => {
|
|
156
|
+
editorService?.remove(node);
|
|
157
|
+
};
|
|
158
|
+
</script>
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-page-bar" ref="pageBar">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
id="m-editor-page-bar-add-icon"
|
|
6
|
-
class="m-editor-page-bar-item m-editor-page-bar-item-icon"
|
|
7
|
-
@click="addPage"
|
|
8
|
-
>
|
|
9
|
-
<Icon :icon="Plus"></Icon>
|
|
10
|
-
</div>
|
|
11
|
-
<div v-else style="width: 21px"></div>
|
|
3
|
+
<slot name="prepend"></slot>
|
|
4
|
+
|
|
12
5
|
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('left')">
|
|
13
6
|
<Icon :icon="ArrowLeftBold"></Icon>
|
|
14
7
|
</div>
|
|
8
|
+
|
|
15
9
|
<div
|
|
16
|
-
v-if="pageLength"
|
|
10
|
+
v-if="(type === NodeType.PAGE && pageLength) || (type === NodeType.PAGE_FRAGMENT && pageFragmentLength)"
|
|
17
11
|
class="m-editor-page-bar-items"
|
|
18
12
|
ref="itemsContainer"
|
|
19
13
|
:style="`width: ${itemsContainerWidth}px`"
|
|
20
14
|
>
|
|
21
15
|
<slot></slot>
|
|
22
16
|
</div>
|
|
17
|
+
|
|
23
18
|
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('right')">
|
|
24
19
|
<Icon :icon="ArrowRightBold"></Icon>
|
|
25
20
|
</div>
|
|
@@ -27,19 +22,32 @@
|
|
|
27
22
|
</template>
|
|
28
23
|
|
|
29
24
|
<script setup lang="ts">
|
|
30
|
-
import {
|
|
31
|
-
|
|
25
|
+
import {
|
|
26
|
+
computed,
|
|
27
|
+
type ComputedRef,
|
|
28
|
+
inject,
|
|
29
|
+
nextTick,
|
|
30
|
+
onBeforeUnmount,
|
|
31
|
+
onMounted,
|
|
32
|
+
ref,
|
|
33
|
+
watch,
|
|
34
|
+
type WatchStopHandle,
|
|
35
|
+
} from 'vue';
|
|
36
|
+
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
|
|
32
37
|
|
|
33
38
|
import { NodeType } from '@tmagic/schema';
|
|
34
39
|
|
|
35
40
|
import Icon from '@editor/components/Icon.vue';
|
|
36
41
|
import type { Services } from '@editor/type';
|
|
37
|
-
import { generatePageNameByApp } from '@editor/utils/editor';
|
|
38
42
|
|
|
39
43
|
defineOptions({
|
|
40
44
|
name: 'MEditorPageBarScrollContainer',
|
|
41
45
|
});
|
|
42
46
|
|
|
47
|
+
const props = defineProps<{
|
|
48
|
+
type: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
|
49
|
+
}>();
|
|
50
|
+
|
|
43
51
|
const services = inject<Services>('services');
|
|
44
52
|
const editorService = services?.editorService;
|
|
45
53
|
const uiService = services?.uiService;
|
|
@@ -72,7 +80,7 @@ onMounted(() => {
|
|
|
72
80
|
pageBar.value && resizeObserver.observe(pageBar.value);
|
|
73
81
|
});
|
|
74
82
|
|
|
75
|
-
|
|
83
|
+
onBeforeUnmount(() => {
|
|
76
84
|
resizeObserver.disconnect();
|
|
77
85
|
});
|
|
78
86
|
|
|
@@ -104,27 +112,45 @@ const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
|
|
|
104
112
|
itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
|
|
105
113
|
};
|
|
106
114
|
|
|
107
|
-
const pageLength = computed(() => editorService?.get('pageLength'));
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
const pageLength = computed(() => editorService?.get('pageLength') || 0);
|
|
116
|
+
const pageFragmentLength = computed(() => editorService?.get('pageFragmentLength') || 0);
|
|
117
|
+
|
|
118
|
+
const crateWatchLength = (length: ComputedRef<number>) =>
|
|
119
|
+
watch(
|
|
120
|
+
length,
|
|
121
|
+
(length = 0, preLength = 0) => {
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
setCanScroll();
|
|
124
|
+
if (length < preLength) {
|
|
125
|
+
scroll('start');
|
|
126
|
+
} else {
|
|
127
|
+
scroll('end');
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
immediate: true,
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
let unWatchPageLength: WatchStopHandle | null;
|
|
137
|
+
let unWatchPageFragmentLength: WatchStopHandle | null;
|
|
138
|
+
|
|
139
|
+
watch(
|
|
140
|
+
() => props.type,
|
|
141
|
+
(type) => {
|
|
142
|
+
if (type === NodeType.PAGE) {
|
|
143
|
+
unWatchPageFragmentLength?.();
|
|
144
|
+
unWatchPageFragmentLength = null;
|
|
145
|
+
unWatchPageLength = crateWatchLength(pageLength);
|
|
114
146
|
} else {
|
|
115
|
-
|
|
147
|
+
unWatchPageLength?.();
|
|
148
|
+
unWatchPageLength = null;
|
|
149
|
+
unWatchPageFragmentLength = crateWatchLength(pageFragmentLength);
|
|
116
150
|
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const root = toRaw(editorService.get('root'));
|
|
123
|
-
if (!root) throw new Error('root 不能为空');
|
|
124
|
-
const pageConfig = {
|
|
125
|
-
type: NodeType.PAGE,
|
|
126
|
-
name: generatePageNameByApp(root),
|
|
127
|
-
};
|
|
128
|
-
editorService.add(pageConfig);
|
|
129
|
-
};
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
immediate: true,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
130
156
|
</script>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TMagicButton
|
|
3
|
+
v-for="item in data"
|
|
4
|
+
class="m-editor-page-bar-switch-type-button"
|
|
5
|
+
size="small"
|
|
6
|
+
:key="item.type"
|
|
7
|
+
text
|
|
8
|
+
:class="{ active: modelValue === item.type }"
|
|
9
|
+
:type="modelValue === item.type ? 'primary' : ''"
|
|
10
|
+
@click="clickHandler(item.type)"
|
|
11
|
+
>{{ item.text }}</TMagicButton
|
|
12
|
+
>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
import { TMagicButton } from '@tmagic/design';
|
|
17
|
+
import { NodeType } from '@tmagic/schema';
|
|
18
|
+
|
|
19
|
+
defineOptions({
|
|
20
|
+
name: 'MEditorPageBarSwitchTypeButton',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
defineProps<{
|
|
24
|
+
modelValue: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
|
25
|
+
}>();
|
|
26
|
+
|
|
27
|
+
const data: { type: NodeType.PAGE | NodeType.PAGE_FRAGMENT; text: string }[] = [
|
|
28
|
+
{
|
|
29
|
+
type: NodeType.PAGE,
|
|
30
|
+
text: '页面',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: NodeType.PAGE_FRAGMENT,
|
|
34
|
+
text: '页面片',
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits<{
|
|
39
|
+
'update:modelValue': [value: NodeType.PAGE | NodeType.PAGE_FRAGMENT];
|
|
40
|
+
}>();
|
|
41
|
+
|
|
42
|
+
const clickHandler = (value: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => {
|
|
43
|
+
emit('update:modelValue', value);
|
|
44
|
+
};
|
|
45
|
+
</script>
|
|
@@ -160,6 +160,7 @@ const props = withDefaults(
|
|
|
160
160
|
defineProps<{
|
|
161
161
|
data: SideBarData;
|
|
162
162
|
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
163
|
+
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
|
163
164
|
}>(),
|
|
164
165
|
{
|
|
165
166
|
data: () => ({ type: 'tabs', status: '组件', items: ['component-list', 'layer', 'code-block', 'data-source'] }),
|
|
@@ -187,6 +188,7 @@ const getItemConfig = (data: SideItem): SideComponent => {
|
|
|
187
188
|
text: '已选组件',
|
|
188
189
|
props: {
|
|
189
190
|
layerContentMenu: props.layerContentMenu,
|
|
191
|
+
customContentMenu: props.customContentMenu,
|
|
190
192
|
},
|
|
191
193
|
component: LayerPanel,
|
|
192
194
|
slots: {},
|
|
@@ -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);
|
|
@@ -34,7 +34,12 @@
|
|
|
34
34
|
</Tree>
|
|
35
35
|
|
|
36
36
|
<Teleport to="body">
|
|
37
|
-
<LayerMenu
|
|
37
|
+
<LayerMenu
|
|
38
|
+
ref="menu"
|
|
39
|
+
:layer-content-menu="layerContentMenu"
|
|
40
|
+
:custom-content-menu="customContentMenu"
|
|
41
|
+
@collapse-all="collapseAllHandler"
|
|
42
|
+
></LayerMenu>
|
|
38
43
|
</Teleport>
|
|
39
44
|
</TMagicScrollbar>
|
|
40
45
|
</template>
|
|
@@ -65,6 +70,7 @@ defineOptions({
|
|
|
65
70
|
|
|
66
71
|
defineProps<{
|
|
67
72
|
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
73
|
+
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
|
68
74
|
}>();
|
|
69
75
|
|
|
70
76
|
const services = inject<Services>('services');
|
|
@@ -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,13 +1,13 @@
|
|
|
1
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 = (page: MPage,
|
|
10
|
+
const createPageNodeStatus = (page: MPage | MPageFragment, initialLayerNodeStatus?: Map<Id, LayerNodeStatus>) => {
|
|
11
11
|
const map = new Map<Id, LayerNodeStatus>();
|
|
12
12
|
|
|
13
13
|
map.set(page.id, {
|
|
@@ -21,7 +21,7 @@ const createPageNodeStatus = (page: MPage, initalLayerNodeStatus?: Map<Id, Layer
|
|
|
21
21
|
traverseNode<MNode>(node, (node) => {
|
|
22
22
|
map.set(
|
|
23
23
|
node.id,
|
|
24
|
-
|
|
24
|
+
initialLayerNodeStatus?.get(node.id) || {
|
|
25
25
|
visible: true,
|
|
26
26
|
expand: false,
|
|
27
27
|
selected: false,
|