@tmagic/editor 1.7.13 → 1.7.14-beta.1
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/es/Editor.vue_vue_type_script_setup_true_lang.js +21 -3
- package/dist/es/components/ScrollViewer.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +8 -4
- package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +19 -9
- package/dist/es/editorProps.js +1 -0
- package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/hooks/use-stage.js +5 -0
- package/dist/es/index.js +2 -2
- package/dist/es/initService.js +3 -0
- package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +12 -3
- package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +21 -5
- package/dist/es/layouts/sidebar/layer/use-click.js +22 -3
- package/dist/es/layouts/sidebar/layer/use-drag.js +28 -6
- package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +87 -12
- package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +16 -4
- package/dist/es/services/editor.js +2 -1
- package/dist/es/services/props.js +14 -2
- package/dist/es/style.css +20 -0
- package/dist/es/utils/dep/worker.js +1 -1
- package/dist/es/utils/tree.js +3 -1
- package/dist/style.css +20 -0
- package/dist/tmagic-editor.umd.cjs +266 -52
- package/package.json +7 -7
- package/src/Editor.vue +17 -0
- package/src/components/ScrollViewer.vue +4 -1
- package/src/components/Tree.vue +5 -1
- package/src/components/TreeNode.vue +14 -9
- package/src/editorProps.ts +23 -0
- package/src/fields/StyleSetter/Index.vue +5 -1
- package/src/hooks/use-stage.ts +9 -0
- package/src/initService.ts +10 -0
- package/src/layouts/props-panel/PropsPanel.vue +1 -1
- package/src/layouts/sidebar/Sidebar.vue +19 -0
- package/src/layouts/sidebar/layer/LayerPanel.vue +37 -3
- package/src/layouts/sidebar/layer/use-click.ts +36 -3
- package/src/layouts/sidebar/layer/use-drag.ts +62 -6
- package/src/layouts/workspace/Breadcrumb.vue +87 -6
- package/src/layouts/workspace/Workspace.vue +3 -1
- package/src/layouts/workspace/viewer/Stage.vue +31 -3
- package/src/services/editor.ts +1 -0
- package/src/services/props.ts +18 -2
- package/src/theme/breadcrumb.scss +24 -0
- package/src/type.ts +58 -1
- package/src/utils/tree.ts +5 -1
- package/types/index.d.ts +97 -8
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.7.
|
|
2
|
+
"version": "1.7.14-beta.1",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"moveable": "^0.53.0",
|
|
59
59
|
"serialize-javascript": "^7.0.0",
|
|
60
60
|
"sortablejs": "^1.15.6",
|
|
61
|
-
"@tmagic/
|
|
62
|
-
"@tmagic/
|
|
63
|
-
"@tmagic/
|
|
64
|
-
"@tmagic/
|
|
65
|
-
"@tmagic/utils": "1.7.
|
|
61
|
+
"@tmagic/design": "1.7.14-beta.1",
|
|
62
|
+
"@tmagic/form": "1.7.14-beta.1",
|
|
63
|
+
"@tmagic/table": "1.7.14-beta.1",
|
|
64
|
+
"@tmagic/stage": "1.7.14-beta.1",
|
|
65
|
+
"@tmagic/utils": "1.7.14-beta.1"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/events": "^3.0.3",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"type-fest": "^5.2.0",
|
|
77
77
|
"typescript": "^6.0.3",
|
|
78
78
|
"vue": "^3.5.33",
|
|
79
|
-
"@tmagic/core": "1.7.
|
|
79
|
+
"@tmagic/core": "1.7.14-beta.1"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
package/src/Editor.vue
CHANGED
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
:custom-content-menu="customContentMenu"
|
|
27
27
|
:indent="treeIndent"
|
|
28
28
|
:next-level-indent-increment="treeNextLevelIndentIncrement"
|
|
29
|
+
:layer-node-is-expandable="layerNodeIsExpandable"
|
|
30
|
+
:can-drop-in="canDropIn"
|
|
31
|
+
:before-layer-node-dblclick="beforeLayerNodeDblclick"
|
|
32
|
+
@layer-node-dblclick="layerNodeDblclickHandler"
|
|
29
33
|
>
|
|
30
34
|
<template #layer-panel-header>
|
|
31
35
|
<slot name="layer-panel-header"></slot>
|
|
@@ -85,6 +89,7 @@
|
|
|
85
89
|
:stage-content-menu="stageContentMenu"
|
|
86
90
|
:custom-content-menu="customContentMenu"
|
|
87
91
|
>
|
|
92
|
+
<template #stage-top><slot name="stage-top"></slot></template>
|
|
88
93
|
<template #stage><slot name="stage"></slot></template>
|
|
89
94
|
<template #workspace-content><slot name="workspace-content" :editorService="editorService"></slot></template>
|
|
90
95
|
</Workspace>
|
|
@@ -154,6 +159,7 @@ import uiService from './services/ui';
|
|
|
154
159
|
import keybindingConfig from './utils/keybinding-config';
|
|
155
160
|
import { defaultEditorProps, EditorProps } from './editorProps';
|
|
156
161
|
import { initServiceEvents, initServiceState } from './initService';
|
|
162
|
+
import type { TreeNodeData } from './type';
|
|
157
163
|
import type { EditorSlots, EventBus, Services, StageOptions } from './type';
|
|
158
164
|
|
|
159
165
|
defineSlots<EditorSlots>();
|
|
@@ -168,6 +174,7 @@ const emit = defineEmits<{
|
|
|
168
174
|
'update:modelValue': [value: MApp | null];
|
|
169
175
|
'props-form-error': [e: any];
|
|
170
176
|
'props-submit-error': [e: any];
|
|
177
|
+
'layer-node-dblclick': [event: MouseEvent, data: TreeNodeData];
|
|
171
178
|
}>();
|
|
172
179
|
|
|
173
180
|
const props = withDefaults(defineProps<EditorProps>(), defaultEditorProps);
|
|
@@ -200,6 +207,11 @@ const stageOptions: StageOptions = {
|
|
|
200
207
|
canSelect: props.canSelect,
|
|
201
208
|
updateDragEl: props.updateDragEl,
|
|
202
209
|
isContainer: props.isContainer,
|
|
210
|
+
// sourceIds 为空表示从组件列表新增(尚无 id),否则是画布上拖动已有组件
|
|
211
|
+
canDropIn: props.canDropIn
|
|
212
|
+
? (sourceIds, targetId) =>
|
|
213
|
+
props.canDropIn!(sourceIds, targetId, sourceIds.length === 0 ? 'stage-add' : 'stage-drag')
|
|
214
|
+
: undefined,
|
|
203
215
|
containerHighlightClassName: props.containerHighlightClassName,
|
|
204
216
|
containerHighlightDuration: props.containerHighlightDuration,
|
|
205
217
|
containerHighlightType: props.containerHighlightType,
|
|
@@ -207,6 +219,7 @@ const stageOptions: StageOptions = {
|
|
|
207
219
|
renderType: props.renderType,
|
|
208
220
|
guidesOptions: props.guidesOptions,
|
|
209
221
|
disabledMultiSelect: props.disabledMultiSelect,
|
|
222
|
+
alwaysMultiSelect: props.alwaysMultiSelect,
|
|
210
223
|
beforeDblclick: props.beforeDblclick,
|
|
211
224
|
};
|
|
212
225
|
|
|
@@ -234,5 +247,9 @@ const propsPanelFormErrorHandler = (e: any) => {
|
|
|
234
247
|
emit('props-form-error', e);
|
|
235
248
|
};
|
|
236
249
|
|
|
250
|
+
const layerNodeDblclickHandler = (event: MouseEvent, data: TreeNodeData) => {
|
|
251
|
+
emit('layer-node-dblclick', event, data);
|
|
252
|
+
};
|
|
253
|
+
|
|
237
254
|
defineExpose(services);
|
|
238
255
|
</script>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-scroll-viewer-container" ref="container">
|
|
3
3
|
<div ref="target" :style="style">
|
|
4
|
+
<slot name="before"></slot>
|
|
4
5
|
<slot></slot>
|
|
5
6
|
</div>
|
|
6
7
|
|
|
@@ -29,7 +30,7 @@ import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from
|
|
|
29
30
|
|
|
30
31
|
import { isNumber } from '@tmagic/utils';
|
|
31
32
|
|
|
32
|
-
import type { ScrollViewerEvent } from '@editor/type';
|
|
33
|
+
import type { ScrollViewerEvent, ScrollViewerSlots } from '@editor/type';
|
|
33
34
|
import { ScrollViewer } from '@editor/utils/scroll-viewer';
|
|
34
35
|
|
|
35
36
|
import ScrollBar from './ScrollBar.vue';
|
|
@@ -38,6 +39,8 @@ defineOptions({
|
|
|
38
39
|
name: 'MEditorScrollViewer',
|
|
39
40
|
});
|
|
40
41
|
|
|
42
|
+
defineSlots<ScrollViewerSlots>();
|
|
43
|
+
|
|
41
44
|
const props = withDefaults(
|
|
42
45
|
defineProps<{
|
|
43
46
|
width?: number | string;
|
package/src/components/Tree.vue
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
:indent="indent"
|
|
9
9
|
:next-level-indent-increment="nextLevelIndentIncrement"
|
|
10
10
|
:node-status-map="nodeStatusMap"
|
|
11
|
+
:is-expandable="isExpandable"
|
|
11
12
|
>
|
|
12
13
|
<template #tree-node-content="{ data: nodeData }">
|
|
13
14
|
<slot name="tree-node-content" :data="nodeData"> </slot>
|
|
@@ -33,7 +34,7 @@ import { provide } from 'vue';
|
|
|
33
34
|
|
|
34
35
|
import type { Id } from '@tmagic/core';
|
|
35
36
|
|
|
36
|
-
import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
|
|
37
|
+
import type { IsExpandableFunction, LayerNodeStatus, TreeNodeData } from '@editor/type';
|
|
37
38
|
|
|
38
39
|
import TreeNode from './TreeNode.vue';
|
|
39
40
|
|
|
@@ -55,6 +56,7 @@ const emit = defineEmits<{
|
|
|
55
56
|
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
|
|
56
57
|
'node-mouseenter': [event: MouseEvent, data: TreeNodeData];
|
|
57
58
|
'node-click': [event: MouseEvent, data: TreeNodeData];
|
|
59
|
+
'node-dblclick': [event: MouseEvent, data: TreeNodeData];
|
|
58
60
|
}>();
|
|
59
61
|
|
|
60
62
|
provide('treeEmit', emit);
|
|
@@ -66,6 +68,8 @@ withDefaults(
|
|
|
66
68
|
indent?: number;
|
|
67
69
|
nextLevelIndentIncrement?: number;
|
|
68
70
|
emptyText?: string;
|
|
71
|
+
/** 自定义判断节点是否可展开(即是否要展示为拥有子节点的形态)的函数 */
|
|
72
|
+
isExpandable?: IsExpandableFunction;
|
|
69
73
|
}>(),
|
|
70
74
|
{
|
|
71
75
|
indent: 0,
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
>
|
|
21
21
|
<MIcon
|
|
22
22
|
class="expand-icon"
|
|
23
|
-
:style="
|
|
23
|
+
:style="isExpandable(data, nodeStatusMap) ? '' : 'color: transparent; cursor: default'"
|
|
24
24
|
:icon="expanded ? ArrowDown : ArrowRight"
|
|
25
25
|
@click="expandHandler"
|
|
26
26
|
></MIcon>
|
|
27
27
|
|
|
28
|
-
<div class="tree-node-content" @click="nodeClickHandler">
|
|
28
|
+
<div class="tree-node-content" @click="nodeClickHandler" @dblclick="nodeDblclickHandler">
|
|
29
29
|
<slot name="tree-node-content" :data="data">
|
|
30
30
|
<div class="tree-node-label">
|
|
31
31
|
<slot name="tree-node-label" :data="data">{{ `${data.name} (${data.id})` }}</slot>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
39
|
|
|
40
|
-
<div v-if="
|
|
40
|
+
<div v-if="isExpandable(data, nodeStatusMap) && expanded" class="m-editor-tree-node-children">
|
|
41
41
|
<TreeNode
|
|
42
42
|
v-for="item in data.items"
|
|
43
43
|
:key="item.id"
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
:parentsId="[...parentsId, data.id]"
|
|
47
47
|
:node-status-map="nodeStatusMap"
|
|
48
48
|
:indent="indent + nextLevelIndentIncrement"
|
|
49
|
+
:is-expandable="isExpandable"
|
|
49
50
|
>
|
|
50
51
|
<template #tree-node-content="{ data: nodeData }">
|
|
51
52
|
<slot name="tree-node-content" :data="nodeData"> </slot>
|
|
@@ -68,8 +69,8 @@ import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
|
|
|
68
69
|
import type { Id } from '@tmagic/core';
|
|
69
70
|
|
|
70
71
|
import MIcon from '@editor/components/Icon.vue';
|
|
71
|
-
import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
|
|
72
|
-
import { updateStatus } from '@editor/utils/tree';
|
|
72
|
+
import type { IsExpandableFunction, LayerNodeStatus, TreeNodeData } from '@editor/type';
|
|
73
|
+
import { defaultIsExpandable, updateStatus } from '@editor/utils/tree';
|
|
73
74
|
|
|
74
75
|
defineSlots<{
|
|
75
76
|
'tree-node-label'(_props: { data: TreeNodeData }): any;
|
|
@@ -88,6 +89,7 @@ const emit = defineEmits<{
|
|
|
88
89
|
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
|
|
89
90
|
'node-mouseenter': [event: MouseEvent, data: TreeNodeData];
|
|
90
91
|
'node-click': [event: MouseEvent, data: TreeNodeData];
|
|
92
|
+
'node-dblclick': [event: MouseEvent, data: TreeNodeData];
|
|
91
93
|
}>();
|
|
92
94
|
|
|
93
95
|
const treeEmit = inject<typeof emit>('treeEmit');
|
|
@@ -100,11 +102,14 @@ const props = withDefaults(
|
|
|
100
102
|
nodeStatusMap: Map<Id, LayerNodeStatus>;
|
|
101
103
|
indent?: number;
|
|
102
104
|
nextLevelIndentIncrement?: number;
|
|
105
|
+
/** 自定义判断节点是否可展开(即是否要展示为拥有子节点的形态)的函数 */
|
|
106
|
+
isExpandable?: IsExpandableFunction;
|
|
103
107
|
}>(),
|
|
104
108
|
{
|
|
105
109
|
indent: 0,
|
|
106
110
|
nextLevelIndentIncrement: 11,
|
|
107
111
|
parentsId: () => [],
|
|
112
|
+
isExpandable: defaultIsExpandable,
|
|
108
113
|
},
|
|
109
114
|
);
|
|
110
115
|
|
|
@@ -123,10 +128,6 @@ const selected = computed(() => nodeStatus.value.selected);
|
|
|
123
128
|
const visible = computed(() => nodeStatus.value.visible);
|
|
124
129
|
const draggable = computed(() => nodeStatus.value.draggable);
|
|
125
130
|
|
|
126
|
-
const hasChildren = computed(
|
|
127
|
-
() => Array.isArray(props.data.items) && props.data.items.some((item) => props.nodeStatusMap.get(item.id)?.visible),
|
|
128
|
-
);
|
|
129
|
-
|
|
130
131
|
const handleDragStart = (event: DragEvent) => {
|
|
131
132
|
treeEmit?.('node-dragstart', event, props.data);
|
|
132
133
|
};
|
|
@@ -156,4 +157,8 @@ const expandHandler = () => {
|
|
|
156
157
|
const nodeClickHandler = (event: MouseEvent) => {
|
|
157
158
|
treeEmit?.('node-click', event, props.data);
|
|
158
159
|
};
|
|
160
|
+
|
|
161
|
+
const nodeDblclickHandler = (event: MouseEvent) => {
|
|
162
|
+
treeEmit?.('node-dblclick', event, props.data);
|
|
163
|
+
};
|
|
159
164
|
</script>
|
package/src/editorProps.ts
CHANGED
|
@@ -11,15 +11,18 @@ import StageCore, {
|
|
|
11
11
|
import { getIdFromEl } from '@tmagic/utils';
|
|
12
12
|
|
|
13
13
|
import type {
|
|
14
|
+
CanDropInFunction,
|
|
14
15
|
ComponentGroup,
|
|
15
16
|
CustomContentMenuFunction,
|
|
16
17
|
DatasourceTypeOption,
|
|
18
|
+
IsExpandableFunction,
|
|
17
19
|
MenuBarData,
|
|
18
20
|
MenuButton,
|
|
19
21
|
MenuComponent,
|
|
20
22
|
PageBarSortOptions,
|
|
21
23
|
SideBarData,
|
|
22
24
|
StageRect,
|
|
25
|
+
TreeNodeData,
|
|
23
26
|
} from './type';
|
|
24
27
|
|
|
25
28
|
export interface EditorProps {
|
|
@@ -74,6 +77,11 @@ export interface EditorProps {
|
|
|
74
77
|
guidesOptions?: Partial<GuidesOptions>;
|
|
75
78
|
/** 禁止多选 */
|
|
76
79
|
disabledMultiSelect?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* 始终启用多选模式:开启后无需按住 Ctrl/Meta,点击即多选;
|
|
82
|
+
* 默认 false。当 `disabledMultiSelect` 为 true 时本配置失效
|
|
83
|
+
*/
|
|
84
|
+
alwaysMultiSelect?: boolean;
|
|
77
85
|
/** 禁用页面片 */
|
|
78
86
|
disabledPageFragment?: boolean;
|
|
79
87
|
/** 禁用双击在浮层中单独编辑选中组件 */
|
|
@@ -98,8 +106,22 @@ export interface EditorProps {
|
|
|
98
106
|
isContainer?: (el: HTMLElement) => boolean | Promise<boolean>;
|
|
99
107
|
/** 用于自定义组件树与画布的右键菜单 */
|
|
100
108
|
customContentMenu?: CustomContentMenuFunction;
|
|
109
|
+
/** 用于自定义判断组件树节点是否可展开(即是否要展示为拥有子节点的形态) */
|
|
110
|
+
layerNodeIsExpandable?: IsExpandableFunction;
|
|
111
|
+
/**
|
|
112
|
+
* 用于自定义判断当前正在拖动的源是否可以拖入目标节点内部
|
|
113
|
+
*
|
|
114
|
+
* 同时覆盖以下两类场景,通过第三个参数 scene 区分:
|
|
115
|
+
* - `'layer'` :"已选组件"面板组件树拖动(返回 false 时仅禁用 inner,不影响 before/after)
|
|
116
|
+
* - `'stage'`:画布拖入组件(返回 false 时阻止该容器被高亮命中;适用于"组件列表拖入新组件"和"画布上拖动已有组件"两种细分情况)
|
|
117
|
+
*
|
|
118
|
+
* 注意:layer 场景目前只识别同步返回值;返回 Promise 时会按 true 处理(即允许)
|
|
119
|
+
*/
|
|
120
|
+
canDropIn?: CanDropInFunction;
|
|
101
121
|
/** 画布双击前的钩子函数,返回 false 则阻止默认的双击行为 */
|
|
102
122
|
beforeDblclick?: (event: MouseEvent) => Promise<boolean | void> | boolean | void;
|
|
123
|
+
/** 组件树节点双击前的钩子函数,返回 false 则阻止默认的双击行为 */
|
|
124
|
+
beforeLayerNodeDblclick?: (event: MouseEvent, data: TreeNodeData) => Promise<boolean | void> | boolean | void;
|
|
103
125
|
extendFormState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
104
126
|
/** 页面顺序拖拽配置参数 */
|
|
105
127
|
pageBarSortOptions?: PageBarSortOptions;
|
|
@@ -110,6 +132,7 @@ export interface EditorProps {
|
|
|
110
132
|
export const defaultEditorProps = {
|
|
111
133
|
renderType: RenderType.IFRAME,
|
|
112
134
|
disabledMultiSelect: false,
|
|
135
|
+
alwaysMultiSelect: false,
|
|
113
136
|
disabledPageFragment: false,
|
|
114
137
|
disabledStageOverlay: false,
|
|
115
138
|
containerHighlightClassName: CONTAINER_HIGHLIGHT_CLASS_NAME,
|
|
@@ -74,7 +74,11 @@ const collapseValue = shallowRef(
|
|
|
74
74
|
|
|
75
75
|
const change = (v: any, eventData: ContainerChangeEventData) => {
|
|
76
76
|
eventData.changeRecords?.forEach((record) => {
|
|
77
|
-
|
|
77
|
+
if (props.prop) {
|
|
78
|
+
record.propPath = `${props.prop}.${record.propPath}`;
|
|
79
|
+
} else if (props.name) {
|
|
80
|
+
record.propPath = `${props.name}.${record.propPath}`;
|
|
81
|
+
}
|
|
78
82
|
});
|
|
79
83
|
emit('change', v, eventData);
|
|
80
84
|
};
|
package/src/hooks/use-stage.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
24
24
|
zoom: stageOptions.zoom ?? zoom.value,
|
|
25
25
|
autoScrollIntoView: stageOptions.autoScrollIntoView,
|
|
26
26
|
isContainer: stageOptions.isContainer,
|
|
27
|
+
canDropIn: stageOptions.canDropIn,
|
|
27
28
|
containerHighlightClassName: stageOptions.containerHighlightClassName,
|
|
28
29
|
containerHighlightDuration: stageOptions.containerHighlightDuration,
|
|
29
30
|
containerHighlightType: stageOptions.containerHighlightType,
|
|
@@ -45,6 +46,7 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
45
46
|
updateDragEl: stageOptions.updateDragEl,
|
|
46
47
|
guidesOptions: stageOptions.guidesOptions,
|
|
47
48
|
disabledMultiSelect: stageOptions.disabledMultiSelect,
|
|
49
|
+
alwaysMultiSelect: stageOptions.alwaysMultiSelect,
|
|
48
50
|
disabledRule: stageOptions.disabledRule,
|
|
49
51
|
});
|
|
50
52
|
|
|
@@ -59,6 +61,13 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
59
61
|
},
|
|
60
62
|
);
|
|
61
63
|
|
|
64
|
+
watch(
|
|
65
|
+
() => editorService.get('alwaysMultiSelect'),
|
|
66
|
+
(alwaysMultiSelect) => {
|
|
67
|
+
stage.setAlwaysMultiSelect(Boolean(alwaysMultiSelect));
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
62
71
|
const hGuidesCache = getGuideLineFromCache(getGuideLineKey(H_GUIDE_LINE_STORAGE_KEY));
|
|
63
72
|
const vGuidesCache = getGuideLineFromCache(getGuideLineKey(V_GUIDE_LINE_STORAGE_KEY));
|
|
64
73
|
|
package/src/initService.ts
CHANGED
|
@@ -72,6 +72,16 @@ export const initServiceState = (
|
|
|
72
72
|
},
|
|
73
73
|
);
|
|
74
74
|
|
|
75
|
+
watch(
|
|
76
|
+
() => props.alwaysMultiSelect,
|
|
77
|
+
(alwaysMultiSelect) => {
|
|
78
|
+
editorService.set('alwaysMultiSelect', alwaysMultiSelect || false);
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
immediate: true,
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
75
85
|
watch(
|
|
76
86
|
() => props.componentGroupList,
|
|
77
87
|
(componentGroupList) => componentGroupList && componentListService.setList(componentGroupList),
|
|
@@ -115,7 +115,7 @@ const init = async () => {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
const type = node.value.type || (node.value.items ? 'container' : 'text');
|
|
118
|
-
curFormConfig.value = await propsService.getPropsConfig(type);
|
|
118
|
+
curFormConfig.value = await propsService.getPropsConfig(type, { node: node.value });
|
|
119
119
|
values.value = node.value;
|
|
120
120
|
};
|
|
121
121
|
|
|
@@ -162,8 +162,10 @@ import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height'
|
|
|
162
162
|
import { useFloatBox } from '@editor/hooks/use-float-box';
|
|
163
163
|
import { useServices } from '@editor/hooks/use-services';
|
|
164
164
|
import {
|
|
165
|
+
type CanDropInFunction,
|
|
165
166
|
ColumnLayout,
|
|
166
167
|
CustomContentMenuFunction,
|
|
168
|
+
type IsExpandableFunction,
|
|
167
169
|
type MenuButton,
|
|
168
170
|
type MenuComponent,
|
|
169
171
|
type SideBarData,
|
|
@@ -171,6 +173,7 @@ import {
|
|
|
171
173
|
type SideComponent,
|
|
172
174
|
type SideItem,
|
|
173
175
|
SideItemKey,
|
|
176
|
+
type TreeNodeData,
|
|
174
177
|
} from '@editor/type';
|
|
175
178
|
|
|
176
179
|
import CodeBlockListPanel from './code-block/CodeBlockListPanel.vue';
|
|
@@ -184,6 +187,10 @@ defineOptions({
|
|
|
184
187
|
name: 'MEditorSidebar',
|
|
185
188
|
});
|
|
186
189
|
|
|
190
|
+
const emit = defineEmits<{
|
|
191
|
+
'layer-node-dblclick': [event: MouseEvent, data: TreeNodeData];
|
|
192
|
+
}>();
|
|
193
|
+
|
|
187
194
|
const props = withDefaults(
|
|
188
195
|
defineProps<{
|
|
189
196
|
data?: SideBarData;
|
|
@@ -191,6 +198,12 @@ const props = withDefaults(
|
|
|
191
198
|
indent?: number;
|
|
192
199
|
nextLevelIndentIncrement?: number;
|
|
193
200
|
customContentMenu: CustomContentMenuFunction;
|
|
201
|
+
/** 自定义判断组件树节点是否可展开(即是否要展示为拥有子节点的形态)的函数 */
|
|
202
|
+
layerNodeIsExpandable?: IsExpandableFunction;
|
|
203
|
+
/** 自定义判断当前正在拖动的源是否可以拖入目标节点内部,详见 EditorProps.canDropIn */
|
|
204
|
+
canDropIn?: CanDropInFunction;
|
|
205
|
+
/** 组件树节点双击前的钩子函数,返回 false 则阻止默认的双击行为 */
|
|
206
|
+
beforeLayerNodeDblclick?: (_event: MouseEvent, _data: TreeNodeData) => Promise<boolean | void> | boolean | void;
|
|
194
207
|
}>(),
|
|
195
208
|
{
|
|
196
209
|
data: () => ({
|
|
@@ -248,6 +261,12 @@ const getItemConfig = (data: SideItem): SideComponent => {
|
|
|
248
261
|
customContentMenu: props.customContentMenu,
|
|
249
262
|
indent: props.indent,
|
|
250
263
|
nextLevelIndentIncrement: props.nextLevelIndentIncrement,
|
|
264
|
+
isExpandable: props.layerNodeIsExpandable,
|
|
265
|
+
canDropIn: props.canDropIn,
|
|
266
|
+
beforeNodeDblclick: props.beforeLayerNodeDblclick,
|
|
267
|
+
},
|
|
268
|
+
listeners: {
|
|
269
|
+
'node-dblclick': (event: MouseEvent, nodeData: TreeNodeData) => emit('layer-node-dblclick', event, nodeData),
|
|
251
270
|
},
|
|
252
271
|
component: LayerPanel,
|
|
253
272
|
slots: {},
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
:node-status-map="nodeStatusMap"
|
|
13
13
|
:indent="indent"
|
|
14
14
|
:next-level-indent-increment="nextLevelIndentIncrement"
|
|
15
|
+
:is-expandable="isExpandable"
|
|
15
16
|
@node-dragover="handleDragOver"
|
|
16
17
|
@node-dragstart="handleDragStart"
|
|
17
18
|
@node-dragleave="handleDragLeave"
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
@node-contextmenu="nodeContentMenuHandler"
|
|
20
21
|
@node-mouseenter="mouseenterHandler"
|
|
21
22
|
@node-click="nodeClickHandler"
|
|
23
|
+
@node-dblclick="dblclickHandler"
|
|
22
24
|
>
|
|
23
25
|
<template #tree-node-content="{ data: nodeData }">
|
|
24
26
|
<slot name="layer-node-content" :data="nodeData"> </slot>
|
|
@@ -56,7 +58,15 @@ import SearchInput from '@editor/components/SearchInput.vue';
|
|
|
56
58
|
import Tree from '@editor/components/Tree.vue';
|
|
57
59
|
import { useFilter } from '@editor/hooks/use-filter';
|
|
58
60
|
import { useServices } from '@editor/hooks/use-services';
|
|
59
|
-
import type {
|
|
61
|
+
import type {
|
|
62
|
+
CanDropInFunction,
|
|
63
|
+
CustomContentMenuFunction,
|
|
64
|
+
IsExpandableFunction,
|
|
65
|
+
LayerPanelSlots,
|
|
66
|
+
MenuButton,
|
|
67
|
+
MenuComponent,
|
|
68
|
+
TreeNodeData,
|
|
69
|
+
} from '@editor/type';
|
|
60
70
|
|
|
61
71
|
import LayerMenu from './LayerMenu.vue';
|
|
62
72
|
import LayerNodeTool from './LayerNodeTool.vue';
|
|
@@ -71,11 +81,21 @@ defineOptions({
|
|
|
71
81
|
name: 'MEditorLayerPanel',
|
|
72
82
|
});
|
|
73
83
|
|
|
74
|
-
defineProps<{
|
|
84
|
+
const props = defineProps<{
|
|
75
85
|
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
76
86
|
indent?: number;
|
|
77
87
|
nextLevelIndentIncrement?: number;
|
|
78
88
|
customContentMenu: CustomContentMenuFunction;
|
|
89
|
+
/** 自定义判断组件树节点是否可展开(即是否要展示为拥有子节点的形态)的函数 */
|
|
90
|
+
isExpandable?: IsExpandableFunction;
|
|
91
|
+
/** 自定义判断当前拖动节点是否可以拖入目标节点内部的函数,返回 false 则禁止拖入 */
|
|
92
|
+
canDropIn?: CanDropInFunction;
|
|
93
|
+
/** 组件树节点双击前的钩子函数,返回 false 则阻止默认的双击行为 */
|
|
94
|
+
beforeNodeDblclick?: (_event: MouseEvent, _data: TreeNodeData) => Promise<boolean | void> | boolean | void;
|
|
95
|
+
}>();
|
|
96
|
+
|
|
97
|
+
const emit = defineEmits<{
|
|
98
|
+
'node-dblclick': [event: MouseEvent, data: TreeNodeData];
|
|
79
99
|
}>();
|
|
80
100
|
|
|
81
101
|
const services = useServices();
|
|
@@ -113,13 +133,27 @@ const collapseAllHandler = () => {
|
|
|
113
133
|
}
|
|
114
134
|
};
|
|
115
135
|
|
|
116
|
-
const { handleDragStart, handleDragEnd, handleDragLeave, handleDragOver } = useDrag(services
|
|
136
|
+
const { handleDragStart, handleDragEnd, handleDragLeave, handleDragOver } = useDrag(services, {
|
|
137
|
+
canDropIn: (sourceIds, targetId) => props.canDropIn?.(sourceIds, targetId, 'layer'),
|
|
138
|
+
});
|
|
117
139
|
|
|
118
140
|
// 右键菜单
|
|
119
141
|
const menuRef = useTemplateRef<InstanceType<typeof LayerMenu>>('menu');
|
|
120
142
|
const {
|
|
121
143
|
nodeClickHandler,
|
|
144
|
+
nodeDblclickHandler,
|
|
122
145
|
nodeContentMenuHandler,
|
|
123
146
|
highlightHandler: mouseenterHandler,
|
|
124
147
|
} = useClick(services, isCtrlKeyDown, nodeStatusMap, menuRef);
|
|
148
|
+
|
|
149
|
+
const dblclickHandler = async (event: MouseEvent, data: TreeNodeData) => {
|
|
150
|
+
if (props.beforeNodeDblclick) {
|
|
151
|
+
const result = await props.beforeNodeDblclick(event, data);
|
|
152
|
+
if (result === false) return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
nodeDblclickHandler(event, data);
|
|
156
|
+
|
|
157
|
+
emit('node-dblclick', event, data);
|
|
158
|
+
};
|
|
125
159
|
</script>
|
|
@@ -2,7 +2,7 @@ import { computed, type ComputedRef, nextTick, type Ref, type ShallowRef } from
|
|
|
2
2
|
import { throttle } from 'lodash-es';
|
|
3
3
|
|
|
4
4
|
import { Id, MNode } from '@tmagic/core';
|
|
5
|
-
import { isPage, isPageFragment } from '@tmagic/utils';
|
|
5
|
+
import { getElById, isPage, isPageFragment } from '@tmagic/utils';
|
|
6
6
|
|
|
7
7
|
import type { LayerNodeStatus, Services, TreeNodeData } from '@editor/type';
|
|
8
8
|
import { UI_SELECT_MODE_EVENT_NAME } from '@editor/utils/const';
|
|
@@ -16,7 +16,9 @@ export const useClick = (
|
|
|
16
16
|
nodeStatusMap: ComputedRef<Map<Id, LayerNodeStatus> | undefined>,
|
|
17
17
|
menuRef: ShallowRef<InstanceType<typeof LayerMenu> | null>,
|
|
18
18
|
) => {
|
|
19
|
-
const isMultiSelect = computed(
|
|
19
|
+
const isMultiSelect = computed(
|
|
20
|
+
() => !editorService.get('disabledMultiSelect') && (isCtrlKeyDown.value || editorService.get('alwaysMultiSelect')),
|
|
21
|
+
);
|
|
20
22
|
|
|
21
23
|
// 触发画布选中
|
|
22
24
|
const select = async (data: MNode) => {
|
|
@@ -81,6 +83,19 @@ export const useClick = (
|
|
|
81
83
|
stageOverlayService.get('stage')?.highlight(data.id);
|
|
82
84
|
};
|
|
83
85
|
|
|
86
|
+
const isNodeCanSelect = async (data: TreeNodeData) => {
|
|
87
|
+
const canSelect = stageOverlayService.get('stageOptions')?.canSelect;
|
|
88
|
+
if (!canSelect) return true;
|
|
89
|
+
|
|
90
|
+
const doc = editorService.get('stage')?.renderer?.contentWindow?.document;
|
|
91
|
+
if (!doc) return true;
|
|
92
|
+
|
|
93
|
+
const el = getElById()(doc, data.id);
|
|
94
|
+
if (!el) return true;
|
|
95
|
+
|
|
96
|
+
return Boolean(await canSelect(el));
|
|
97
|
+
};
|
|
98
|
+
|
|
84
99
|
const nodeClickHandler = (event: MouseEvent, data: TreeNodeData): void => {
|
|
85
100
|
if (!nodeStatusMap?.value) return;
|
|
86
101
|
|
|
@@ -95,16 +110,34 @@ export const useClick = (
|
|
|
95
110
|
});
|
|
96
111
|
}
|
|
97
112
|
|
|
98
|
-
nextTick(() => {
|
|
113
|
+
nextTick(async () => {
|
|
114
|
+
if (!(await isNodeCanSelect(data))) return;
|
|
115
|
+
|
|
99
116
|
select(data);
|
|
100
117
|
});
|
|
101
118
|
};
|
|
102
119
|
|
|
120
|
+
const nodeDblclickHandler = (event: MouseEvent, data: TreeNodeData): void => {
|
|
121
|
+
if (!nodeStatusMap?.value) return;
|
|
122
|
+
|
|
123
|
+
if (uiService.get('uiSelectMode')) return;
|
|
124
|
+
|
|
125
|
+
// 双击切换展开/收起状态
|
|
126
|
+
if (data.items && data.items.length > 0) {
|
|
127
|
+
const status = nodeStatusMap.value.get(data.id);
|
|
128
|
+
updateStatus(nodeStatusMap.value, data.id, {
|
|
129
|
+
expand: !status?.expand,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
103
134
|
return {
|
|
104
135
|
menuRef,
|
|
105
136
|
|
|
106
137
|
nodeClickHandler,
|
|
107
138
|
|
|
139
|
+
nodeDblclickHandler,
|
|
140
|
+
|
|
108
141
|
nodeContentMenuHandler(event: MouseEvent, data: TreeNodeData): void {
|
|
109
142
|
event.preventDefault();
|
|
110
143
|
|