@tmagic/editor 1.3.0-alpha.6 → 1.3.0-alpha.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/dist/style.css +17 -0
- package/dist/tmagic-editor.js +471 -264
- package/dist/tmagic-editor.js.map +1 -1
- package/dist/tmagic-editor.umd.cjs +474 -265
- package/dist/tmagic-editor.umd.cjs.map +1 -1
- package/package.json +12 -12
- package/src/Editor.vue +3 -0
- package/src/components/ContentMenu.vue +15 -8
- package/src/components/FunctionEditor.vue +6 -4
- package/src/components/SplitView.vue +2 -2
- package/src/fields/CodeLink.vue +4 -2
- package/src/fields/DataSourceInput.vue +11 -10
- package/src/index.ts +4 -2
- package/src/initService.ts +2 -0
- package/src/layouts/Framework.vue +4 -4
- package/src/layouts/PropsPanel.vue +2 -1
- package/src/layouts/sidebar/LayerMenu.vue +4 -3
- package/src/layouts/sidebar/LayerPanel.vue +55 -32
- package/src/layouts/workspace/Stage.vue +13 -3
- package/src/layouts/workspace/Workspace.vue +2 -2
- package/src/services/codeBlock.ts +3 -2
- package/src/services/keybinding.ts +148 -203
- package/src/services/storage.ts +3 -2
- package/src/theme/data-source-input.scss +18 -0
- package/src/theme/stage.scss +4 -0
- package/src/theme/theme.scss +1 -0
- package/src/type.ts +56 -0
- package/src/utils/config.ts +1 -1
- package/src/utils/keybinding-config.ts +120 -0
- package/src/utils/props.ts +34 -0
- package/types/components/ContentMenu.vue.d.ts +2 -0
- package/types/index.d.ts +2 -1
- package/types/initService.d.ts +1 -1
- package/types/layouts/workspace/Workspace.vue.d.ts +1 -1
- package/types/services/keybinding.d.ts +13 -5
- package/types/type.d.ts +53 -0
- package/types/utils/config.d.ts +1 -1
- package/types/utils/keybinding-config.d.ts +3 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.3.0-alpha.
|
|
2
|
+
"version": "1.3.0-alpha.8",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -46,27 +46,27 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@babel/core": "^7.18.0",
|
|
48
48
|
"@element-plus/icons-vue": "^2.0.9",
|
|
49
|
-
"@tmagic/core": "1.3.0-alpha.
|
|
50
|
-
"@tmagic/design": "1.3.0-alpha.
|
|
51
|
-
"@tmagic/form": "1.3.0-alpha.
|
|
52
|
-
"@tmagic/schema": "1.3.0-alpha.
|
|
53
|
-
"@tmagic/stage": "1.3.0-alpha.
|
|
54
|
-
"@tmagic/table": "1.3.0-alpha.
|
|
55
|
-
"@tmagic/utils": "1.3.0-alpha.
|
|
49
|
+
"@tmagic/core": "1.3.0-alpha.8",
|
|
50
|
+
"@tmagic/design": "1.3.0-alpha.8",
|
|
51
|
+
"@tmagic/form": "1.3.0-alpha.8",
|
|
52
|
+
"@tmagic/schema": "1.3.0-alpha.8",
|
|
53
|
+
"@tmagic/stage": "1.3.0-alpha.8",
|
|
54
|
+
"@tmagic/table": "1.3.0-alpha.8",
|
|
55
|
+
"@tmagic/utils": "1.3.0-alpha.8",
|
|
56
56
|
"buffer": "^6.0.3",
|
|
57
57
|
"color": "^3.1.3",
|
|
58
58
|
"events": "^3.3.0",
|
|
59
59
|
"gesto": "^1.7.0",
|
|
60
60
|
"keycon": "^1.1.2",
|
|
61
61
|
"lodash-es": "^4.17.21",
|
|
62
|
-
"monaco-editor": "^0.
|
|
62
|
+
"monaco-editor": "^0.39.0",
|
|
63
63
|
"serialize-javascript": "^6.0.0",
|
|
64
64
|
"vue": "^3.3.4"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@tmagic/design": "1.3.0-alpha.
|
|
68
|
-
"@tmagic/form": "1.3.0-alpha.
|
|
69
|
-
"monaco-editor": "^0.
|
|
67
|
+
"@tmagic/design": "1.3.0-alpha.8",
|
|
68
|
+
"@tmagic/form": "1.3.0-alpha.8",
|
|
69
|
+
"monaco-editor": "^0.39.0",
|
|
70
70
|
"vue": "^3.3.4"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
package/src/Editor.vue
CHANGED
|
@@ -100,6 +100,7 @@ import keybindingService from './services/keybinding';
|
|
|
100
100
|
import propsService from './services/props';
|
|
101
101
|
import storageService from './services/storage';
|
|
102
102
|
import uiService from './services/ui';
|
|
103
|
+
import keybindingConfig from './utils/keybinding-config';
|
|
103
104
|
import editorProps from './editorProps';
|
|
104
105
|
import { initServiceEvents, initServiceState } from './initService';
|
|
105
106
|
import type { Services } from './type';
|
|
@@ -136,6 +137,8 @@ export default defineComponent({
|
|
|
136
137
|
|
|
137
138
|
initServiceEvents(props, emit, services);
|
|
138
139
|
initServiceState(props, services);
|
|
140
|
+
keybindingService.registe(keybindingConfig);
|
|
141
|
+
keybindingService.registeEl('global');
|
|
139
142
|
|
|
140
143
|
provide('services', services);
|
|
141
144
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
<teleport to="body">
|
|
14
14
|
<content-menu
|
|
15
|
+
v-if="subMenuData.length"
|
|
15
16
|
class="sub-menu"
|
|
16
17
|
ref="subMenu"
|
|
17
18
|
:menu-data="subMenuData"
|
|
@@ -55,6 +56,8 @@ const menuStyle = ref({
|
|
|
55
56
|
top: '0',
|
|
56
57
|
});
|
|
57
58
|
|
|
59
|
+
const contains = (el: HTMLElement) => menu.value?.contains(el) || subMenu.value?.contains(el);
|
|
60
|
+
|
|
58
61
|
const hide = () => {
|
|
59
62
|
if (!visible.value) return;
|
|
60
63
|
|
|
@@ -69,7 +72,7 @@ const hideHandler = (e: MouseEvent) => {
|
|
|
69
72
|
if (!visible.value || !target) {
|
|
70
73
|
return;
|
|
71
74
|
}
|
|
72
|
-
if (
|
|
75
|
+
if (contains(target)) {
|
|
73
76
|
return;
|
|
74
77
|
}
|
|
75
78
|
hide();
|
|
@@ -104,13 +107,15 @@ const showSubMenu = (item: MenuButton | MenuComponent) => {
|
|
|
104
107
|
return;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
subMenuData.value = menuItem.items;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
subMenuData.value = menuItem.items || [];
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
if (menu.value) {
|
|
113
|
+
subMenu.value?.show({
|
|
114
|
+
clientX: menu.value.offsetLeft + menu.value.clientWidth,
|
|
115
|
+
clientY: menu.value.offsetTop,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}, 0);
|
|
114
119
|
};
|
|
115
120
|
|
|
116
121
|
onMounted(() => {
|
|
@@ -126,7 +131,9 @@ onUnmounted(() => {
|
|
|
126
131
|
});
|
|
127
132
|
|
|
128
133
|
defineExpose({
|
|
134
|
+
menu,
|
|
129
135
|
hide,
|
|
130
136
|
show,
|
|
137
|
+
contains,
|
|
131
138
|
});
|
|
132
139
|
</script>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</TMagicCard>
|
|
34
34
|
</template>
|
|
35
35
|
<script lang="ts" setup>
|
|
36
|
-
import { inject, ref, watchEffect } from 'vue';
|
|
36
|
+
import { inject, provide, ref, watchEffect } from 'vue';
|
|
37
37
|
import { cloneDeep } from 'lodash-es';
|
|
38
38
|
|
|
39
39
|
import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
|
|
@@ -41,6 +41,7 @@ import { ColumnConfig, TableConfig } from '@tmagic/form';
|
|
|
41
41
|
import { CodeParam, Id } from '@tmagic/schema';
|
|
42
42
|
|
|
43
43
|
import type { Services } from '@editor/type';
|
|
44
|
+
import { getConfig } from '@editor/utils/config';
|
|
44
45
|
|
|
45
46
|
import CodeDraftEditor from './CodeDraftEditor.vue';
|
|
46
47
|
|
|
@@ -48,6 +49,8 @@ defineOptions({
|
|
|
48
49
|
name: 'MEditorFunctionEditor',
|
|
49
50
|
});
|
|
50
51
|
|
|
52
|
+
provide('mForm', {});
|
|
53
|
+
|
|
51
54
|
const defaultParamColConfig: ColumnConfig = {
|
|
52
55
|
type: 'row',
|
|
53
56
|
label: '参数类型',
|
|
@@ -147,9 +150,8 @@ initTableModel();
|
|
|
147
150
|
// 保存前钩子
|
|
148
151
|
const beforeSave = (codeValue: string): boolean => {
|
|
149
152
|
try {
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
eval(codeValue);
|
|
153
|
+
// 检测js代码是否存在语法错误
|
|
154
|
+
getConfig('parseDSL')(codeValue);
|
|
153
155
|
return true;
|
|
154
156
|
} catch (e: any) {
|
|
155
157
|
tMagicMessage.error(e.stack);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="el" class="m-editor-layout">
|
|
3
|
-
<template v-if="hasLeft">
|
|
3
|
+
<template v-if="hasLeft && $slots.left">
|
|
4
4
|
<div class="m-editor-layout-left" :class="leftClass" :style="`width: ${left}px`">
|
|
5
5
|
<slot name="left"></slot>
|
|
6
6
|
</div>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<slot name="center"></slot>
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
-
<template v-if="hasRight">
|
|
14
|
+
<template v-if="hasRight && $slots.right">
|
|
15
15
|
<Resizer @change="changeRight"></Resizer>
|
|
16
16
|
<div class="m-editor-layout-right" :class="rightClass" :style="`width: ${right}px`">
|
|
17
17
|
<slot name="right"></slot>
|
package/src/fields/CodeLink.vue
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { computed, reactive, watch } from 'vue';
|
|
7
7
|
import serialize from 'serialize-javascript';
|
|
8
8
|
|
|
9
|
+
import { getConfig } from '@editor/utils/config';
|
|
10
|
+
|
|
9
11
|
defineOptions({
|
|
10
12
|
name: 'MEditorCodeLink',
|
|
11
13
|
});
|
|
@@ -69,8 +71,8 @@ const changeHandler = (v: Record<string, any>) => {
|
|
|
69
71
|
if (!props.name || !props.model) return;
|
|
70
72
|
|
|
71
73
|
try {
|
|
72
|
-
|
|
73
|
-
props.model[props.name] =
|
|
74
|
+
const parseDSL = getConfig('parseDSL');
|
|
75
|
+
props.model[props.name] = parseDSL(`(${v[props.name]})`);
|
|
74
76
|
emit('change', props.model[props.name]);
|
|
75
77
|
} catch (e) {
|
|
76
78
|
console.error(e);
|
|
@@ -30,13 +30,15 @@
|
|
|
30
30
|
</div>
|
|
31
31
|
</template>
|
|
32
32
|
</component>
|
|
33
|
-
<div :class="`el-input el-input--${size}`" @mouseup="mouseupHandler" v-else>
|
|
34
|
-
<div :class="`el-input__wrapper ${isFocused ? ' is-focus' : ''}`">
|
|
33
|
+
<div :class="`tmagic-data-source-input-text el-input el-input--${size}`" @mouseup="mouseupHandler" v-else>
|
|
34
|
+
<div :class="`tmagic-data-source-input-text-wrapper el-input__wrapper ${isFocused ? ' is-focus' : ''}`">
|
|
35
35
|
<div class="el-input__inner">
|
|
36
36
|
<template v-for="(item, index) in displayState">
|
|
37
37
|
<span :key="index" v-if="item.type === 'text'" style="margin-right: 2px">{{ item.value }}</span>
|
|
38
38
|
<TMagicTag :key="index" :size="size" v-if="item.type === 'var'">{{ item.value }}</TMagicTag>
|
|
39
39
|
</template>
|
|
40
|
+
|
|
41
|
+
<Icon class="tmagic-data-source-input-icon" :icon="Coin" />
|
|
40
42
|
</div>
|
|
41
43
|
</div>
|
|
42
44
|
</div>
|
|
@@ -90,7 +92,7 @@ const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
|
|
|
90
92
|
const setDisplayState = () => {
|
|
91
93
|
displayState.value = [];
|
|
92
94
|
|
|
93
|
-
const matches = state.value.matchAll(
|
|
95
|
+
const matches = state.value.matchAll(/\$\{([\s\S]+?)\}/g);
|
|
94
96
|
let index = 0;
|
|
95
97
|
for (const match of matches) {
|
|
96
98
|
if (typeof match.index === 'undefined') break;
|
|
@@ -195,7 +197,7 @@ const getSelectionStart = () => {
|
|
|
195
197
|
* @param leftCurlyBracketIndex {字符索引
|
|
196
198
|
*/
|
|
197
199
|
const curCharIsLeftCurlyBracket = (leftCurlyBracketIndex: number) =>
|
|
198
|
-
leftCurlyBracketIndex >
|
|
200
|
+
leftCurlyBracketIndex > 0 && leftCurlyBracketIndex === getSelectionStart() - 1;
|
|
199
201
|
|
|
200
202
|
/**
|
|
201
203
|
* 当前输入的是.
|
|
@@ -213,7 +215,7 @@ const dsQuerySearch = (queryString: string, leftCurlyBracketIndex: number, cb: (
|
|
|
213
215
|
if (curCharIsLeftCurlyBracket(leftCurlyBracketIndex)) {
|
|
214
216
|
// 当前输入的是{
|
|
215
217
|
result = dataSources.value;
|
|
216
|
-
} else if (leftCurlyBracketIndex >
|
|
218
|
+
} else if (leftCurlyBracketIndex > 0) {
|
|
217
219
|
// 当前输入的是{xx
|
|
218
220
|
const queryName = queryString.substring(leftCurlyBracketIndex + 1).toLowerCase();
|
|
219
221
|
result = dataSources.value.filter((ds) => ds.title?.toLowerCase().includes(queryName) || ds.id.includes(queryName));
|
|
@@ -299,7 +301,7 @@ const querySearch = (queryString: string, cb: (data: { value: string }[]) => voi
|
|
|
299
301
|
const curQueryString = queryString.substring(0, selectionStart);
|
|
300
302
|
|
|
301
303
|
const fieldKeyStringLastIndex = curQueryString.lastIndexOf('.');
|
|
302
|
-
const dsKeyStringLastIndex = curQueryString.lastIndexOf('{');
|
|
304
|
+
const dsKeyStringLastIndex = curQueryString.lastIndexOf('${') + 1;
|
|
303
305
|
|
|
304
306
|
const isFieldTip = fieldKeyStringLastIndex > dsKeyStringLastIndex;
|
|
305
307
|
|
|
@@ -321,7 +323,7 @@ const selectHandler = async ({ value, type }: { value: string; type: 'dataSource
|
|
|
321
323
|
let startText = inputText.substring(0, selectionStart);
|
|
322
324
|
|
|
323
325
|
const dotIndex = startText.lastIndexOf('.');
|
|
324
|
-
const leftCurlyBracketIndex = startText.lastIndexOf('{');
|
|
326
|
+
const leftCurlyBracketIndex = startText.lastIndexOf('${') + 1;
|
|
325
327
|
|
|
326
328
|
const endText = inputText.substring(selectionStart);
|
|
327
329
|
|
|
@@ -336,7 +338,6 @@ const selectHandler = async ({ value, type }: { value: string; type: 'dataSource
|
|
|
336
338
|
if (!isRightCurlyBracket(selectionStart + 1)) {
|
|
337
339
|
suggestText = `${suggestText}}`;
|
|
338
340
|
}
|
|
339
|
-
suggestText = `{${suggestText}}`;
|
|
340
341
|
} else if (!curCharIsDot(dotIndex)) {
|
|
341
342
|
startText = startText.substring(0, dotIndex + 1);
|
|
342
343
|
}
|
|
@@ -345,10 +346,10 @@ const selectHandler = async ({ value, type }: { value: string; type: 'dataSource
|
|
|
345
346
|
|
|
346
347
|
await nextTick();
|
|
347
348
|
|
|
348
|
-
// 由于选择数据源时会在后面补全}
|
|
349
|
+
// 由于选择数据源时会在后面补全}, 所以光标要前移2位
|
|
349
350
|
let newSelectionStart = 0;
|
|
350
351
|
if (isDataSource) {
|
|
351
|
-
newSelectionStart = leftCurlyBracketIndex + suggestText.length
|
|
352
|
+
newSelectionStart = leftCurlyBracketIndex + suggestText.length;
|
|
352
353
|
} else {
|
|
353
354
|
newSelectionStart = dotIndex + suggestText.length + 1;
|
|
354
355
|
}
|
package/src/index.ts
CHANGED
|
@@ -62,13 +62,15 @@ export { default as ContentMenu } from './components/ContentMenu.vue';
|
|
|
62
62
|
export { default as Icon } from './components/Icon.vue';
|
|
63
63
|
export { default as LayoutContainer } from './components/SplitView.vue';
|
|
64
64
|
export { default as SplitView } from './components/SplitView.vue';
|
|
65
|
+
export { default as Resizer } from './components/Resizer.vue';
|
|
65
66
|
|
|
66
67
|
const defaultInstallOpt: InstallOptions = {
|
|
67
|
-
//
|
|
68
|
+
// eslint-disable-next-line no-eval
|
|
69
|
+
parseDSL: (dsl: string) => eval(dsl),
|
|
68
70
|
};
|
|
69
71
|
|
|
70
72
|
export default {
|
|
71
|
-
install: (app: App, opt?: InstallOptions): void => {
|
|
73
|
+
install: (app: App, opt?: Partial<InstallOptions>): void => {
|
|
72
74
|
const option = Object.assign(defaultInstallOpt, opt || {});
|
|
73
75
|
|
|
74
76
|
// eslint-disable-next-line no-param-reassign
|
package/src/initService.ts
CHANGED
|
@@ -25,6 +25,7 @@ export const initServiceState = (
|
|
|
25
25
|
eventsService,
|
|
26
26
|
uiService,
|
|
27
27
|
codeBlockService,
|
|
28
|
+
keybindingService,
|
|
28
29
|
}: Services,
|
|
29
30
|
) => {
|
|
30
31
|
// 初始值变化,重新设置节点信息
|
|
@@ -104,6 +105,7 @@ export const initServiceState = (
|
|
|
104
105
|
uiService.resetState();
|
|
105
106
|
componentListService.resetState();
|
|
106
107
|
codeBlockService.resetState();
|
|
108
|
+
keybindingService.reset();
|
|
107
109
|
});
|
|
108
110
|
};
|
|
109
111
|
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</slot>
|
|
34
34
|
</template>
|
|
35
35
|
|
|
36
|
-
<template v-if="pageLength > 0
|
|
36
|
+
<template v-if="pageLength > 0" #right>
|
|
37
37
|
<TMagicScrollbar>
|
|
38
38
|
<slot name="props-panel"></slot>
|
|
39
39
|
</TMagicScrollbar>
|
|
@@ -52,6 +52,7 @@ import { TMagicScrollbar } from '@tmagic/design';
|
|
|
52
52
|
|
|
53
53
|
import SplitView from '@editor/components/SplitView.vue';
|
|
54
54
|
import type { GetColumnWidth, Services } from '@editor/type';
|
|
55
|
+
import { getConfig } from '@editor/utils/config';
|
|
55
56
|
|
|
56
57
|
import AddPageBox from './AddPageBox.vue';
|
|
57
58
|
import CodeEditor from './CodeEditor.vue';
|
|
@@ -67,7 +68,6 @@ const codeOptions = inject('codeOptions', {});
|
|
|
67
68
|
const { editorService, uiService } = inject<Services>('services') || {};
|
|
68
69
|
|
|
69
70
|
const root = computed(() => editorService?.get('root'));
|
|
70
|
-
const nodes = computed(() => editorService?.get('nodes') || []);
|
|
71
71
|
|
|
72
72
|
const pageLength = computed(() => editorService?.get('pageLength') || 0);
|
|
73
73
|
const showSrc = computed(() => uiService?.get('showSrc'));
|
|
@@ -131,8 +131,8 @@ const columnWidthChange = (columnW: GetColumnWidth) => {
|
|
|
131
131
|
|
|
132
132
|
const saveCode = (value: string) => {
|
|
133
133
|
try {
|
|
134
|
-
|
|
135
|
-
editorService?.set('root',
|
|
134
|
+
const parseDSL = getConfig('parseDSL');
|
|
135
|
+
editorService?.set('root', parseDSL(value));
|
|
136
136
|
} catch (e: any) {
|
|
137
137
|
console.error(e);
|
|
138
138
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="m-editor-props-panel">
|
|
2
|
+
<div class="m-editor-props-panel" v-if="nodes.length === 1">
|
|
3
3
|
<slot name="props-panel-header"></slot>
|
|
4
4
|
<MForm
|
|
5
5
|
ref="configForm"
|
|
@@ -35,6 +35,7 @@ const configForm = ref<InstanceType<typeof MForm>>();
|
|
|
35
35
|
const curFormConfig = ref<any>([]);
|
|
36
36
|
const services = inject<Services>('services');
|
|
37
37
|
const node = computed(() => services?.editorService.get('node'));
|
|
38
|
+
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
|
38
39
|
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small');
|
|
39
40
|
const stage = computed(() => services?.editorService.get('stage'));
|
|
40
41
|
|
|
@@ -22,6 +22,7 @@ const props = defineProps<{
|
|
|
22
22
|
const services = inject<Services>('services');
|
|
23
23
|
const menu = ref<InstanceType<typeof ContentMenu>>();
|
|
24
24
|
const node = computed(() => services?.editorService.get('node'));
|
|
25
|
+
const nodes = computed(() => services?.editorService.get('nodes'));
|
|
25
26
|
const isRoot = computed(() => node.value?.type === NodeType.ROOT);
|
|
26
27
|
const isPage = computed(() => node.value?.type === NodeType.PAGE);
|
|
27
28
|
const componentList = computed(() => services?.componentListService.getList() || []);
|
|
@@ -82,7 +83,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
82
83
|
type: 'button',
|
|
83
84
|
text: '新增',
|
|
84
85
|
icon: markRaw(Plus),
|
|
85
|
-
display: () => node.value?.items,
|
|
86
|
+
display: () => node.value?.items && nodes.value?.length === 1,
|
|
86
87
|
items: getSubMenuData.value,
|
|
87
88
|
},
|
|
88
89
|
{
|
|
@@ -91,7 +92,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
91
92
|
icon: markRaw(CopyDocument),
|
|
92
93
|
display: () => !isRoot.value,
|
|
93
94
|
handler: () => {
|
|
94
|
-
node.value && services?.editorService.copy(
|
|
95
|
+
node.value && services?.editorService.copy(nodes.value || []);
|
|
95
96
|
},
|
|
96
97
|
},
|
|
97
98
|
{
|
|
@@ -100,7 +101,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
100
101
|
icon: markRaw(Delete),
|
|
101
102
|
display: () => !isRoot.value && !isPage.value,
|
|
102
103
|
handler: () => {
|
|
103
|
-
node.value && services?.editorService.remove(
|
|
104
|
+
node.value && services?.editorService.remove(nodes.value || []);
|
|
104
105
|
},
|
|
105
106
|
},
|
|
106
107
|
...props.layerContentMenu,
|
|
@@ -50,8 +50,7 @@
|
|
|
50
50
|
</template>
|
|
51
51
|
|
|
52
52
|
<script lang="ts" setup>
|
|
53
|
-
import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
54
|
-
import KeyController from 'keycon';
|
|
53
|
+
import { computed, inject, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
55
54
|
import { difference, throttle, union } from 'lodash-es';
|
|
56
55
|
|
|
57
56
|
import { TMagicScrollbar, TMagicTree } from '@tmagic/design';
|
|
@@ -248,51 +247,71 @@ const windowBlurHandler = () => {
|
|
|
248
247
|
isCtrlKeyDown.value = false;
|
|
249
248
|
};
|
|
250
249
|
|
|
251
|
-
|
|
252
|
-
if (
|
|
250
|
+
keybindingService?.registeCommand('layer-panel-not-ctrl-keydown', (e) => {
|
|
251
|
+
if (e.key !== keybindingService.ctrlKey) {
|
|
253
252
|
isCtrlKeyDown.value = false;
|
|
254
253
|
}
|
|
255
|
-
};
|
|
254
|
+
});
|
|
256
255
|
|
|
257
|
-
|
|
256
|
+
keybindingService?.registeCommand('layer-panel-ctrl-keydown', () => {
|
|
257
|
+
isCtrlKeyDown.value = true;
|
|
258
|
+
});
|
|
258
259
|
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
keybindingService?.registeCommand('layer-panel-ctrl-keyup', () => {
|
|
261
|
+
isCtrlKeyDown.value = false;
|
|
262
|
+
});
|
|
261
263
|
|
|
264
|
+
keybindingService?.registeCommand('layer-panel-global-keydwon', () => {
|
|
265
|
+
if (!tree.value?.$el.contains(document.activeElement)) {
|
|
266
|
+
isCtrlKeyDown.value = false;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
keybindingService?.registe([
|
|
271
|
+
{
|
|
272
|
+
command: 'layer-panel-not-ctrl-keydown',
|
|
273
|
+
when: [['layer-panel', 'keydown']],
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
command: 'layer-panel-ctrl-keydown',
|
|
277
|
+
keybinding: 'ctrl',
|
|
278
|
+
when: [['layer-panel', 'keydown']],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
command: 'layer-panel-ctrl-keyup',
|
|
282
|
+
keybinding: 'ctrl',
|
|
283
|
+
when: [['layer-panel', 'keyup']],
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
command: 'layer-panel-global-keydwon',
|
|
287
|
+
keybinding: 'ctrl',
|
|
288
|
+
when: [['global', 'keydown']],
|
|
289
|
+
},
|
|
290
|
+
]);
|
|
291
|
+
|
|
292
|
+
watch(tree, () => {
|
|
262
293
|
if (tree.value?.$el) {
|
|
263
|
-
|
|
264
|
-
keybindingService?.on('keyup', globalKeyupHandler);
|
|
265
|
-
|
|
266
|
-
keycon = new KeyController(tree.value.$el);
|
|
267
|
-
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
|
|
268
|
-
const ctrl = isMac ? 'meta' : 'ctrl';
|
|
269
|
-
|
|
270
|
-
keycon
|
|
271
|
-
.keydown((e) => {
|
|
272
|
-
if (e.key !== ctrl) {
|
|
273
|
-
isCtrlKeyDown.value = false;
|
|
274
|
-
}
|
|
275
|
-
})
|
|
276
|
-
.keydown(ctrl, () => {
|
|
277
|
-
isCtrlKeyDown.value = true;
|
|
278
|
-
})
|
|
279
|
-
.keyup(ctrl, () => {
|
|
280
|
-
isCtrlKeyDown.value = false;
|
|
281
|
-
});
|
|
294
|
+
keybindingService?.registeEl('layer-panel', tree.value.$el);
|
|
282
295
|
|
|
283
296
|
tree.value.$el.addEventListener('blur', windowBlurHandler);
|
|
297
|
+
} else {
|
|
298
|
+
keybindingService?.unregisteEl('layer-panel');
|
|
284
299
|
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
onMounted(() => {
|
|
303
|
+
editorService?.on('remove', editorServiceRemoveHandler);
|
|
285
304
|
|
|
286
305
|
globalThis.addEventListener('blur', windowBlurHandler);
|
|
287
306
|
});
|
|
288
307
|
|
|
289
|
-
|
|
290
|
-
|
|
308
|
+
onBeforeUnmount(() => {
|
|
309
|
+
tree.value?.$el.removeEventListener('blur', windowBlurHandler);
|
|
310
|
+
});
|
|
291
311
|
|
|
312
|
+
onUnmounted(() => {
|
|
292
313
|
editorService?.off('remove', editorServiceRemoveHandler);
|
|
293
|
-
keybindingService?.off('keyup', globalKeyupHandler);
|
|
294
314
|
globalThis.removeEventListener('blur', windowBlurHandler);
|
|
295
|
-
tree.value?.$el.removeEventListener('blur', windowBlurHandler);
|
|
296
315
|
});
|
|
297
316
|
|
|
298
317
|
// 鼠标在组件树移动触发高亮
|
|
@@ -331,7 +350,11 @@ const clickHandler = (data: MNode): void => {
|
|
|
331
350
|
// 右键菜单
|
|
332
351
|
const contextmenu = async (event: MouseEvent, data: MNode): Promise<void> => {
|
|
333
352
|
event.preventDefault();
|
|
334
|
-
|
|
353
|
+
|
|
354
|
+
if (nodes.value.length < 2) {
|
|
355
|
+
await select(data);
|
|
356
|
+
}
|
|
357
|
+
|
|
335
358
|
menu.value?.show(event);
|
|
336
359
|
};
|
|
337
360
|
</script>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<ScrollViewer
|
|
3
3
|
class="m-editor-stage"
|
|
4
4
|
ref="stageWrap"
|
|
5
|
+
tabindex="-1"
|
|
5
6
|
:width="stageRect?.width"
|
|
6
7
|
:height="stageRect?.height"
|
|
7
8
|
:wrap-width="stageContainerRect?.width"
|
|
@@ -35,6 +36,7 @@ import StageCore, { calcValueByFontsize, getOffset, Runtime } from '@tmagic/stag
|
|
|
35
36
|
|
|
36
37
|
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
|
37
38
|
import { Layout, MenuButton, MenuComponent, Services, StageOptions } from '@editor/type';
|
|
39
|
+
import { getConfig } from '@editor/utils/config';
|
|
38
40
|
import { useStage } from '@editor/utils/stage';
|
|
39
41
|
|
|
40
42
|
import ViewerMenu from './ViewerMenu.vue';
|
|
@@ -74,6 +76,10 @@ watchEffect(() => {
|
|
|
74
76
|
|
|
75
77
|
stage = useStage(stageOptions);
|
|
76
78
|
|
|
79
|
+
stage.on('select', () => {
|
|
80
|
+
stageWrap.value?.container?.focus();
|
|
81
|
+
});
|
|
82
|
+
|
|
77
83
|
services?.editorService.set('stage', markRaw(stage));
|
|
78
84
|
|
|
79
85
|
stage?.mount(stageContainer.value);
|
|
@@ -120,13 +126,17 @@ const resizeObserver = new ResizeObserver((entries) => {
|
|
|
120
126
|
});
|
|
121
127
|
|
|
122
128
|
onMounted(() => {
|
|
123
|
-
|
|
129
|
+
if (stageWrap.value?.container) {
|
|
130
|
+
resizeObserver.observe(stageWrap.value.container);
|
|
131
|
+
services?.keybindingService.registeEl('stage', stageWrap.value.container);
|
|
132
|
+
}
|
|
124
133
|
});
|
|
125
134
|
|
|
126
135
|
onUnmounted(() => {
|
|
127
136
|
stage?.destroy();
|
|
128
137
|
resizeObserver.disconnect();
|
|
129
138
|
services?.editorService.set('stage', null);
|
|
139
|
+
services?.keybindingService.unregisteEl('stage');
|
|
130
140
|
});
|
|
131
141
|
|
|
132
142
|
const contextmenuHandler = (e: MouseEvent) => {
|
|
@@ -153,8 +163,8 @@ const dropHandler = async (e: DragEvent) => {
|
|
|
153
163
|
}
|
|
154
164
|
|
|
155
165
|
if (e.dataTransfer && parent && stageContainer.value && stage) {
|
|
156
|
-
|
|
157
|
-
const config =
|
|
166
|
+
const parseDSL = getConfig('parseDSL');
|
|
167
|
+
const config = parseDSL(`(${e.dataTransfer.getData('data')})`);
|
|
158
168
|
const layout = await services?.editorService.getLayout(parent);
|
|
159
169
|
|
|
160
170
|
const containerRect = stageContainer.value.getBoundingClientRect();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="m-editor-workspace"
|
|
2
|
+
<div class="m-editor-workspace">
|
|
3
3
|
<Breadcrumb></Breadcrumb>
|
|
4
4
|
|
|
5
5
|
<slot name="stage">
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script lang="ts" setup>
|
|
19
19
|
import { computed, inject } from 'vue';
|
|
20
20
|
|
|
21
|
-
import
|
|
21
|
+
import { MenuButton, MenuComponent, Services } from '@editor/type';
|
|
22
22
|
|
|
23
23
|
import Breadcrumb from './Breadcrumb.vue';
|
|
24
24
|
import PageBar from './PageBar.vue';
|
|
@@ -23,6 +23,7 @@ import { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
|
|
23
23
|
|
|
24
24
|
import type { CodeState } from '@editor/type';
|
|
25
25
|
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
|
26
|
+
import { getConfig } from '@editor/utils/config';
|
|
26
27
|
|
|
27
28
|
import BaseService from './BaseService';
|
|
28
29
|
|
|
@@ -95,8 +96,8 @@ class CodeBlock extends BaseService {
|
|
|
95
96
|
const codeConfigProcessed = codeConfig;
|
|
96
97
|
if (codeConfig.content) {
|
|
97
98
|
// 在保存的时候转换代码内容
|
|
98
|
-
|
|
99
|
-
codeConfigProcessed.content =
|
|
99
|
+
const parseDSL = getConfig('parseDSL');
|
|
100
|
+
codeConfigProcessed.content = parseDSL(codeConfig.content);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
const existContent = codeDsl[id] || {};
|