@tmagic/editor 1.3.0-alpha.8 → 1.3.0-beta.0

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.
Files changed (124) hide show
  1. package/dist/style.css +82 -218
  2. package/dist/tmagic-editor.js +4985 -3999
  3. package/dist/tmagic-editor.js.map +1 -1
  4. package/dist/tmagic-editor.umd.cjs +5013 -4012
  5. package/dist/tmagic-editor.umd.cjs.map +1 -1
  6. package/package.json +15 -15
  7. package/src/Editor.vue +4 -5
  8. package/src/components/CodeBlockEditor.vue +257 -0
  9. package/src/components/CodeParams.vue +60 -0
  10. package/src/components/ContentMenu.vue +11 -3
  11. package/src/editorProps.ts +30 -5
  12. package/src/fields/Code.vue +23 -19
  13. package/src/fields/CodeLink.vue +6 -7
  14. package/src/fields/CodeSelect.vue +12 -9
  15. package/src/fields/CodeSelectCol.vue +70 -81
  16. package/src/fields/DataSourceFieldSelect.vue +47 -0
  17. package/src/fields/DataSourceFields.vue +28 -22
  18. package/src/fields/DataSourceInput.vue +21 -58
  19. package/src/fields/DataSourceMethodSelect.vue +136 -0
  20. package/src/fields/DataSourceMethods.vue +103 -0
  21. package/src/fields/DataSourceSelect.vue +68 -0
  22. package/src/fields/EventSelect.vue +53 -19
  23. package/src/fields/KeyValue.vue +9 -11
  24. package/src/fields/UISelect.vue +54 -14
  25. package/src/hooks/index.ts +21 -0
  26. package/src/hooks/use-code-block-edit.ts +84 -0
  27. package/src/hooks/use-data-source-method.ts +100 -0
  28. package/src/{utils/stage.ts → hooks/use-stage.ts} +4 -7
  29. package/src/icons/CenterIcon.vue +13 -0
  30. package/src/icons/CodeIcon.vue +0 -2
  31. package/src/icons/FolderMinusIcon.vue +22 -0
  32. package/src/index.ts +15 -0
  33. package/src/initService.ts +73 -16
  34. package/src/layouts/CodeEditor.vue +62 -10
  35. package/src/layouts/Framework.vue +7 -3
  36. package/src/layouts/PropsPanel.vue +8 -3
  37. package/src/layouts/sidebar/ComponentListPanel.vue +6 -9
  38. package/src/layouts/sidebar/LayerMenu.vue +20 -21
  39. package/src/layouts/sidebar/LayerNode.vue +40 -0
  40. package/src/layouts/sidebar/LayerPanel.vue +14 -5
  41. package/src/layouts/sidebar/Sidebar.vue +22 -36
  42. package/src/layouts/sidebar/code-block/CodeBlockList.vue +81 -119
  43. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +64 -0
  44. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +27 -47
  45. package/src/layouts/sidebar/data-source/DataSourceList.vue +145 -0
  46. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +58 -69
  47. package/src/layouts/workspace/Stage.vue +13 -5
  48. package/src/layouts/workspace/ViewerMenu.vue +10 -35
  49. package/src/services/codeBlock.ts +18 -71
  50. package/src/services/dataSource.ts +16 -1
  51. package/src/services/dep.ts +23 -20
  52. package/src/services/editor.ts +46 -18
  53. package/src/services/props.ts +2 -2
  54. package/src/theme/code-block.scss +5 -117
  55. package/src/theme/code-editor.scss +27 -2
  56. package/src/theme/content-menu.scss +1 -1
  57. package/src/theme/data-source-methods.scss +13 -0
  58. package/src/theme/data-source.scss +11 -0
  59. package/src/theme/event.scss +15 -12
  60. package/src/theme/framework.scss +0 -3
  61. package/src/theme/layer-panel.scss +6 -0
  62. package/src/theme/layout.scss +4 -0
  63. package/src/theme/sidebar.scss +18 -60
  64. package/src/theme/theme.scss +1 -0
  65. package/src/type.ts +47 -7
  66. package/src/utils/content-menu.ts +92 -0
  67. package/src/utils/data-source/formConfigs/base.ts +28 -30
  68. package/src/utils/data-source/index.ts +65 -2
  69. package/src/utils/dep.ts +39 -7
  70. package/src/utils/index.ts +1 -1
  71. package/src/utils/props.ts +298 -189
  72. package/src/utils/undo-redo.ts +1 -1
  73. package/types/Editor.vue.d.ts +30 -6
  74. package/types/components/CodeBlockEditor.vue.d.ts +23 -0
  75. package/types/components/CodeParams.vue.d.ts +26 -0
  76. package/types/components/ContentMenu.vue.d.ts +2 -2
  77. package/types/components/ToolButton.vue.d.ts +3 -3
  78. package/types/editorProps.d.ts +19 -5
  79. package/types/fields/Code.vue.d.ts +24 -19
  80. package/types/fields/CodeLink.vue.d.ts +12 -21
  81. package/types/fields/CodeSelect.vue.d.ts +6 -17
  82. package/types/fields/CodeSelectCol.vue.d.ts +10 -14
  83. package/types/fields/DataSourceFieldSelect.vue.d.ts +29 -0
  84. package/types/fields/DataSourceFields.vue.d.ts +7 -18
  85. package/types/fields/DataSourceInput.vue.d.ts +10 -30
  86. package/types/fields/DataSourceMethodSelect.vue.d.ts +29 -0
  87. package/types/fields/DataSourceMethods.vue.d.ts +32 -0
  88. package/types/fields/DataSourceSelect.vue.d.ts +40 -0
  89. package/types/fields/EventSelect.vue.d.ts +2 -13
  90. package/types/fields/KeyValue.vue.d.ts +9 -24
  91. package/types/fields/UISelect.vue.d.ts +2 -11
  92. package/types/hooks/index.d.ts +3 -0
  93. package/types/{components/FunctionEditor.vue.d.ts → hooks/use-code-block-edit.d.ts} +31 -119
  94. package/types/hooks/use-data-source-method.d.ts +123 -0
  95. package/types/icons/CenterIcon.vue.d.ts +2 -0
  96. package/types/icons/FolderMinusIcon.vue.d.ts +2 -0
  97. package/types/index.d.ts +7 -0
  98. package/types/initService.d.ts +2 -2
  99. package/types/layouts/CodeEditor.vue.d.ts +10 -4
  100. package/types/layouts/PropsPanel.vue.d.ts +274 -2
  101. package/types/layouts/sidebar/LayerNode.vue.d.ts +16 -0
  102. package/types/layouts/sidebar/Sidebar.vue.d.ts +0 -3
  103. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +4 -9
  104. package/types/layouts/sidebar/code-block/{CodeBlockEditor.vue.d.ts → CodeBlockListPanel.vue.d.ts} +9 -5
  105. package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +3 -1
  106. package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +14 -0
  107. package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +1 -11
  108. package/types/services/codeBlock.d.ts +6 -37
  109. package/types/services/dataSource.d.ts +7 -1
  110. package/types/services/dep.d.ts +12 -21
  111. package/types/services/editor.d.ts +1 -1
  112. package/types/services/props.d.ts +10 -115
  113. package/types/type.d.ts +42 -7
  114. package/types/utils/content-menu.d.ts +7 -0
  115. package/types/utils/data-source/formConfigs/base.d.ts +1 -2
  116. package/types/utils/data-source/index.d.ts +5 -0
  117. package/types/utils/dep.d.ts +4 -2
  118. package/types/utils/index.d.ts +1 -1
  119. package/types/utils/props.d.ts +13 -102
  120. package/src/components/CodeDraftEditor.vue +0 -148
  121. package/src/components/FunctionEditor.vue +0 -197
  122. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +0 -100
  123. package/types/components/CodeDraftEditor.vue.d.ts +0 -61
  124. /package/types/{utils/stage.d.ts → hooks/use-stage.d.ts} +0 -0
@@ -1,12 +1,12 @@
1
1
  import { computed } from 'vue';
2
2
 
3
+ import type { MNode } from '@tmagic/schema';
3
4
  import StageCore, { GuidesType, RemoveEventData, SortEventData, UpdateEventData } from '@tmagic/stage';
4
5
 
5
6
  import editorService from '@editor/services/editor';
6
7
  import uiService from '@editor/services/ui';
7
8
  import { H_GUIDE_LINE_STORAGE_KEY, StageOptions, V_GUIDE_LINE_STORAGE_KEY } from '@editor/type';
8
-
9
- import { getGuideLineFromCache } from './editor';
9
+ import { getGuideLineFromCache } from '@editor/utils/editor';
10
10
 
11
11
  const root = computed(() => editorService.get('root'));
12
12
  const page = computed(() => editorService.get('page'));
@@ -74,11 +74,8 @@ export const useStage = (stageOptions: StageOptions) => {
74
74
  });
75
75
 
76
76
  stage.on('remove', (ev: RemoveEventData) => {
77
- editorService.remove(
78
- ev.data.map(({ el }) => ({
79
- id: el.id,
80
- })),
81
- );
77
+ const nodes = ev.data.map(({ el }) => editorService.getNodeById(el.id));
78
+ editorService.remove(nodes.filter((node) => Boolean(node)) as MNode[]);
82
79
  });
83
80
 
84
81
  stage.on('select-parent', () => {
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M2 4H21V6H2V4Z" fill="black" fill-opacity="0.9" />
4
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M5 11H18V13H5V11Z" fill="black" fill-opacity="0.9" />
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M2 18H21V20H2V18Z" fill="black" fill-opacity="0.9" />
6
+ </svg>
7
+ </template>
8
+
9
+ <script lang="ts" setup>
10
+ defineOptions({
11
+ name: 'MEditorCenterIcon',
12
+ });
13
+ </script>
@@ -1,7 +1,5 @@
1
1
  <template>
2
- <!-- 代码icon,cdn链接:https://cloudcache.tencent-cloud.com/qcloud/ui/static/government/0d463ed5-6407-4498-8865-3d05b5e70115.svg -->
3
2
  <svg viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
4
- <!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
5
3
  <defs><rect id="path-1" x="0" y="0" width="32" height="32"></rect></defs>
6
4
  <g id="组件规范" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
7
5
  <g id="03图标" transform="translate(-561.000000, -2356.000000)">
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <svg
3
+ width="1em"
4
+ height="1em"
5
+ viewBox="0 0 16 16"
6
+ class="bi bi-folder-minus"
7
+ fill="currentColor"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ >
10
+ <path
11
+ fill-rule="evenodd"
12
+ d="M9.828 4H2.19a1 1 0 0 0-.996 1.09l.637 7a1 1 0 0 0 .995.91H9v1H2.826a2 2 0 0 1-1.991-1.819l-.637-7a1.99 1.99 0 0 1 .342-1.31L.5 3a2 2 0 0 1 2-2h3.672a2 2 0 0 1 1.414.586l.828.828A2 2 0 0 0 9.828 3h3.982a2 2 0 0 1 1.992 2.181L15.546 8H14.54l.265-2.91A1 1 0 0 0 13.81 4H9.828zm-2.95-1.707L7.587 3H2.19c-.24 0-.47.042-.684.12L1.5 2.98a1 1 0 0 1 1-.98h3.672a1 1 0 0 1 .707.293z"
13
+ />
14
+ <path fill-rule="evenodd" d="M11 11.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z" />
15
+ </svg>
16
+ </template>
17
+
18
+ <script lang="ts" setup>
19
+ defineOptions({
20
+ name: 'MEditorFolderMinusIcon',
21
+ });
22
+ </script>
package/src/index.ts CHANGED
@@ -22,7 +22,11 @@ import CodeLink from './fields/CodeLink.vue';
22
22
  import CodeSelect from './fields/CodeSelect.vue';
23
23
  import CodeSelectCol from './fields/CodeSelectCol.vue';
24
24
  import DataSourceFields from './fields/DataSourceFields.vue';
25
+ import DataSourceFieldSelect from './fields/DataSourceFieldSelect.vue';
25
26
  import DataSourceInput from './fields/DataSourceInput.vue';
27
+ import DataSourceMethods from './fields/DataSourceMethods.vue';
28
+ import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
29
+ import DataSourceSelect from './fields/DataSourceSelect.vue';
26
30
  import EventSelect from './fields/EventSelect.vue';
27
31
  import KeyValue from './fields/KeyValue.vue';
28
32
  import uiSelect from './fields/UISelect.vue';
@@ -35,6 +39,7 @@ import './theme/index.scss';
35
39
 
36
40
  export type { MoveableOptions } from '@tmagic/stage';
37
41
  export * from './type';
42
+ export * from './hooks';
38
43
  export * from './utils';
39
44
  export { default as TMagicEditor } from './Editor.vue';
40
45
  export { default as TMagicCodeEditor } from './layouts/CodeEditor.vue';
@@ -52,10 +57,15 @@ export { default as LayerPanel } from './layouts/sidebar/LayerPanel.vue';
52
57
  export { default as CodeSelect } from './fields/CodeSelect.vue';
53
58
  export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
54
59
  export { default as DataSourceFields } from './fields/DataSourceFields.vue';
60
+ export { default as DataSourceMethods } from './fields/DataSourceMethods.vue';
55
61
  export { default as DataSourceInput } from './fields/DataSourceInput.vue';
62
+ export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
63
+ export { default as DataSourceMethodSelect } from './fields/DataSourceMethodSelect.vue';
64
+ export { default as DataSourceFieldSelect } from './fields/DataSourceFieldSelect.vue';
56
65
  export { default as EventSelect } from './fields/EventSelect.vue';
57
66
  export { default as KeyValue } from './fields/KeyValue.vue';
58
67
  export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
68
+ export { default as CodeBlockListPanel } from './layouts/sidebar/code-block/CodeBlockListPanel.vue';
59
69
  export { default as PropsPanel } from './layouts/PropsPanel.vue';
60
70
  export { default as ToolButton } from './components/ToolButton.vue';
61
71
  export { default as ContentMenu } from './components/ContentMenu.vue';
@@ -63,6 +73,7 @@ export { default as Icon } from './components/Icon.vue';
63
73
  export { default as LayoutContainer } from './components/SplitView.vue';
64
74
  export { default as SplitView } from './components/SplitView.vue';
65
75
  export { default as Resizer } from './components/Resizer.vue';
76
+ export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
66
77
 
67
78
  const defaultInstallOpt: InstallOptions = {
68
79
  // eslint-disable-next-line no-eval
@@ -87,5 +98,9 @@ export default {
87
98
  app.component('m-fields-data-source-fields', DataSourceFields);
88
99
  app.component('m-fields-key-value', KeyValue);
89
100
  app.component('m-fields-data-source-input', DataSourceInput);
101
+ app.component('m-fields-data-source-select', DataSourceSelect);
102
+ app.component('m-fields-data-source-methods', DataSourceMethods);
103
+ app.component('m-fields-data-source-method-select', DataSourceMethodSelect);
104
+ app.component('m-fields-data-source-field-select', DataSourceFieldSelect);
90
105
  },
91
106
  };
@@ -7,9 +7,14 @@ import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from
7
7
  import { getNodes } from '@tmagic/utils';
8
8
 
9
9
  import type { Target } from './services/dep';
10
- import { createCodeBlockTarget, createDataSourceTarget } from './utils/dep';
10
+ import {
11
+ createCodeBlockTarget,
12
+ createDataSourceCondTarget,
13
+ createDataSourceMethodTarget,
14
+ createDataSourceTarget,
15
+ } from './utils/dep';
11
16
  import editorProps from './editorProps';
12
- import type { Services } from './type';
17
+ import { DepTargetType, Services } from './type';
13
18
 
14
19
  export declare type LooseRequired<T> = {
15
20
  [P in string & keyof T]: T[P];
@@ -26,6 +31,7 @@ export const initServiceState = (
26
31
  uiService,
27
32
  codeBlockService,
28
33
  keybindingService,
34
+ dataSourceService,
29
35
  }: Services,
30
36
  ) => {
31
37
  // 初始值变化,重新设置节点信息
@@ -47,6 +53,14 @@ export const initServiceState = (
47
53
  },
48
54
  );
49
55
 
56
+ watch(
57
+ () => props.datasourceList,
58
+ (datasourceList) => dataSourceService.set('datasourceTypeList', datasourceList),
59
+ {
60
+ immediate: true,
61
+ },
62
+ );
63
+
50
64
  watch(
51
65
  () => props.propsConfigs,
52
66
  (configs) => propsService.setPropsConfigs(configs),
@@ -82,6 +96,30 @@ export const initServiceState = (
82
96
  },
83
97
  );
84
98
 
99
+ watch(
100
+ () => props.datasourceConfigs,
101
+ (configs) => {
102
+ Object.entries(configs).forEach(([key, value]) => {
103
+ dataSourceService.setFormConfig(key, value);
104
+ });
105
+ },
106
+ {
107
+ immediate: true,
108
+ },
109
+ );
110
+
111
+ watch(
112
+ () => props.datasourceValues,
113
+ (values) => {
114
+ Object.entries(values).forEach(([key, value]) => {
115
+ dataSourceService.setFormValue(key, value);
116
+ });
117
+ },
118
+ {
119
+ immediate: true,
120
+ },
121
+ );
122
+
85
123
  watch(
86
124
  () => props.defaultSelected,
87
125
  (defaultSelected) => defaultSelected && editorService.select(defaultSelected),
@@ -139,6 +177,7 @@ export const initServiceEvents = (
139
177
 
140
178
  if (app.dsl) {
141
179
  app.dsl.dataSourceDeps = root.dataSourceDeps;
180
+ app.dsl.dataSourceCondDeps = root.dataSourceCondDeps;
142
181
  app.dsl.dataSources = root.dataSources;
143
182
  }
144
183
 
@@ -159,23 +198,34 @@ export const initServiceEvents = (
159
198
  };
160
199
 
161
200
  const targetAddHandler = (target: Target) => {
162
- if (target.type !== 'data-source') return;
163
-
164
201
  const root = editorService.get('root');
165
202
  if (!root) return;
166
203
 
167
- if (!root.dataSourceDeps) {
168
- root.dataSourceDeps = {};
204
+ if (target.type === DepTargetType.DATA_SOURCE) {
205
+ if (!root.dataSourceDeps) {
206
+ root.dataSourceDeps = {};
207
+ }
208
+ root.dataSourceDeps[target.id] = target.deps;
169
209
  }
170
210
 
171
- root.dataSourceDeps[target.id] = target.deps;
211
+ if (target.type === DepTargetType.DATA_SOURCE_COND) {
212
+ if (!root.dataSourceCondDeps) {
213
+ root.dataSourceCondDeps = {};
214
+ }
215
+ root.dataSourceCondDeps[target.id] = target.deps;
216
+ }
172
217
  };
173
218
 
174
219
  const targetRemoveHandler = (id: string | number) => {
175
220
  const root = editorService.get('root');
176
- if (!root?.dataSourceDeps) return;
177
221
 
178
- delete root.dataSourceDeps[id];
222
+ if (root?.dataSourceDeps) {
223
+ delete root.dataSourceDeps[id];
224
+ }
225
+
226
+ if (root?.dataSourceCondDeps) {
227
+ delete root.dataSourceCondDeps[id];
228
+ }
179
229
  };
180
230
 
181
231
  const depUpdateHandler = (node: MNode) => {
@@ -191,6 +241,12 @@ export const initServiceEvents = (
191
241
  depService.on('dep-update', depUpdateHandler);
192
242
  depService.on('collected', collectedHandler);
193
243
 
244
+ const initDataSourceDepTarget = (ds: DataSourceSchema) => {
245
+ depService.addTarget(createDataSourceTarget(ds.id));
246
+ depService.addTarget(createDataSourceMethodTarget(ds.id));
247
+ depService.addTarget(createDataSourceCondTarget(ds.id));
248
+ };
249
+
194
250
  const rootChangeHandler = async (value: MApp, preValue?: MApp | null) => {
195
251
  const nodeId = editorService.get('node')?.id || props.defaultSelected;
196
252
  let node;
@@ -218,14 +274,14 @@ export const initServiceEvents = (
218
274
  codeBlockService.setCodeDsl(value.codeBlocks);
219
275
  dataSourceService.set('dataSources', value.dataSources);
220
276
 
221
- depService.removeTargets('code-block');
277
+ depService.removeTargets(DepTargetType.CODE_BLOCK);
222
278
 
223
279
  Object.entries(value.codeBlocks).forEach(([id, code]) => {
224
280
  depService.addTarget(createCodeBlockTarget(id, code));
225
281
  });
226
282
 
227
283
  value.dataSources.forEach((ds) => {
228
- depService.addTarget(createDataSourceTarget(ds.id, ds));
284
+ initDataSourceDepTarget(ds);
229
285
  });
230
286
 
231
287
  if (value && Array.isArray(value.items)) {
@@ -236,18 +292,22 @@ export const initServiceEvents = (
236
292
  }
237
293
  };
238
294
 
295
+ // 新增节点,收集依赖
239
296
  const nodeAddHandler = (nodes: MNode[]) => {
240
297
  depService.collect(nodes);
241
298
  };
242
299
 
300
+ // 节点更新,收集依赖
243
301
  const nodeUpdateHandler = (nodes: MNode[]) => {
244
302
  depService.collect(nodes);
245
303
  };
246
304
 
305
+ // 节点删除,清除对齐的依赖收集
247
306
  const nodeRemoveHandler = (nodes: MNode[]) => {
248
307
  depService.clear(nodes);
249
308
  };
250
309
 
310
+ // 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
251
311
  const historyChangeHandler = (page: MPage) => {
252
312
  depService.collect([page], true);
253
313
  };
@@ -275,17 +335,14 @@ export const initServiceEvents = (
275
335
  codeBlockService.on('remove', codeBlockRemoveHandler);
276
336
 
277
337
  const dataSourceAddHandler = (config: DataSourceSchema) => {
278
- depService.addTarget(createDataSourceTarget(config.id, config));
338
+ initDataSourceDepTarget(config);
279
339
  getApp()?.dataSourceManager?.addDataSource(config);
280
340
  };
281
341
 
282
342
  const dataSourceUpdateHandler = (config: DataSourceSchema) => {
283
- if (config.title) {
284
- depService.getTarget(config.id)!.name = config.title;
285
- }
286
343
  const root = editorService.get('root');
287
344
 
288
- const targets = depService.getTargets('data-source');
345
+ const targets = depService.getTargets(DepTargetType.DATA_SOURCE);
289
346
 
290
347
  const nodes = getNodes(Object.keys(targets[config.id].deps), root?.items);
291
348
 
@@ -1,26 +1,46 @@
1
1
  <template>
2
- <div ref="codeEditor" class="magic-code-editor"></div>
2
+ <div :class="`magic-code-editor`">
3
+ <Teleport to="body" :disabled="!fullScreen">
4
+ <div
5
+ :class="`magic-code-editor-wrapper${fullScreen ? ' full-screen' : ''}`"
6
+ :style="!fullScreen && height ? `height: ${height}` : '100%'"
7
+ >
8
+ <TMagicButton
9
+ class="magic-code-editor-full-screen-icon"
10
+ circle
11
+ size="small"
12
+ :icon="FullScreen"
13
+ @click="fullScreenHandler"
14
+ ></TMagicButton>
15
+ <div ref="codeEditor" class="magic-code-editor-content"></div>
16
+ </div>
17
+ </Teleport>
18
+ </div>
3
19
  </template>
4
20
 
5
21
  <script lang="ts" setup>
6
22
  import { onMounted, onUnmounted, ref, watch } from 'vue';
23
+ import { FullScreen } from '@element-plus/icons-vue';
7
24
  import { throttle } from 'lodash-es';
8
25
  import * as monaco from 'monaco-editor';
9
26
  import serialize from 'serialize-javascript';
10
27
 
28
+ import { TMagicButton } from '@tmagic/design';
29
+
11
30
  defineOptions({
12
31
  name: 'MEditorCodeEditor',
13
32
  });
14
33
 
15
34
  const props = withDefaults(
16
35
  defineProps<{
17
- initValues?: string | Record<string | number, any> | null;
18
- modifiedValues?: string | Record<string | number, any> | null;
36
+ initValues?: any;
37
+ modifiedValues?: any;
19
38
  type?: 'diff';
20
39
  language?: string;
21
40
  options?: {
22
41
  [key: string]: any;
23
42
  };
43
+ height?: string;
24
44
  autoSave?: boolean;
25
45
  }>(),
26
46
  {
@@ -37,10 +57,14 @@ const emit = defineEmits(['initd', 'save']);
37
57
  const toString = (v: string | any, language: string): string => {
38
58
  let value = '';
39
59
  if (typeof v !== 'string') {
40
- value = serialize(v, {
41
- space: 2,
42
- unsafe: true,
43
- }).replace(/"(\w+)":\s/g, '$1: ');
60
+ if (props.language.toLocaleLowerCase() === 'json') {
61
+ value = JSON.stringify(v, null, 2);
62
+ } else {
63
+ value = serialize(v, {
64
+ space: 2,
65
+ unsafe: true,
66
+ }).replace(/"(\w+)":\s/g, '$1: ');
67
+ }
44
68
  } else {
45
69
  value = v;
46
70
  }
@@ -81,7 +105,7 @@ const setEditorValue = (v: string | any, m: string | any) => {
81
105
  };
82
106
 
83
107
  const getEditorValue = () =>
84
- props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue();
108
+ (props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue()) || '';
85
109
 
86
110
  const init = async () => {
87
111
  if (!codeEditor.value) return;
@@ -109,13 +133,19 @@ const init = async () => {
109
133
  if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
110
134
  e.preventDefault();
111
135
  e.stopPropagation();
112
- emit('save', getEditorValue());
136
+ const newValue = getEditorValue();
137
+ values.value = newValue;
138
+ emit('save', newValue);
113
139
  }
114
140
  });
115
141
 
116
142
  if (props.type !== 'diff' && props.autoSave) {
117
143
  vsEditor?.onDidBlurEditorWidget(() => {
118
- emit('save', getEditorValue());
144
+ const newValue = getEditorValue();
145
+ if (values.value !== newValue) {
146
+ values.value = newValue;
147
+ emit('save', newValue);
148
+ }
119
149
  });
120
150
  }
121
151
 
@@ -145,12 +175,34 @@ onUnmounted(() => {
145
175
  resizeObserver.disconnect();
146
176
  });
147
177
 
178
+ const fullScreen = ref(false);
179
+ const fullScreenHandler = () => {
180
+ fullScreen.value = !fullScreen.value;
181
+ setTimeout(() => {
182
+ vsEditor?.focus();
183
+ vsEditor?.layout();
184
+ vsDiffEditor?.focus();
185
+ vsDiffEditor?.layout();
186
+ });
187
+ };
188
+
148
189
  defineExpose({
190
+ values,
191
+
149
192
  getEditor() {
150
193
  return vsEditor || vsDiffEditor;
151
194
  },
152
195
 
196
+ getVsEditor() {
197
+ return vsEditor;
198
+ },
199
+
200
+ getVsDiffEditor() {
201
+ return vsDiffEditor;
202
+ },
203
+
153
204
  setEditorValue,
205
+ getEditorValue,
154
206
 
155
207
  focus() {
156
208
  vsEditor?.focus();
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="m-editor">
2
+ <div class="m-editor" ref="content">
3
3
  <slot name="header"></slot>
4
4
 
5
5
  <slot name="nav"></slot>
@@ -67,6 +67,8 @@ const DEFAULT_RIGHT_COLUMN_WIDTH = 480;
67
67
  const codeOptions = inject('codeOptions', {});
68
68
  const { editorService, uiService } = inject<Services>('services') || {};
69
69
 
70
+ const content = ref<HTMLDivElement>();
71
+
70
72
  const root = computed(() => editorService?.get('root'));
71
73
 
72
74
  const pageLength = computed(() => editorService?.get('pageLength') || 0);
@@ -91,13 +93,15 @@ watch(
91
93
 
92
94
  columnWidth.value.left = left;
93
95
 
96
+ const container = content.value || document.body;
97
+
94
98
  if (length <= 0) {
95
99
  columnWidth.value.right = undefined;
96
- columnWidth.value.center = globalThis.document.body.clientWidth - left;
100
+ columnWidth.value.center = container.clientWidth - left;
97
101
  } else {
98
102
  const right = columnWidth.value.right || RightColumnWidthCacheData || DEFAULT_RIGHT_COLUMN_WIDTH;
99
103
  columnWidth.value.right = right;
100
- columnWidth.value.center = globalThis.document.body.clientWidth - left - right;
104
+ columnWidth.value.center = container.clientWidth - left - right;
101
105
  }
102
106
 
103
107
  uiService?.set('columnWidth', columnWidth.value as GetColumnWidth);
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="m-editor-props-panel" v-if="nodes.length === 1">
2
+ <div class="m-editor-props-panel" v-show="nodes.length === 1">
3
3
  <slot name="props-panel-header"></slot>
4
4
  <MForm
5
5
  ref="configForm"
@@ -8,6 +8,7 @@
8
8
  :size="propsPanelSize"
9
9
  :init-values="values"
10
10
  :config="curFormConfig"
11
+ :extend-state="extendState"
11
12
  @change="submit"
12
13
  ></MForm>
13
14
  </div>
@@ -17,7 +18,7 @@
17
18
  import { computed, getCurrentInstance, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue';
18
19
 
19
20
  import { tMagicMessage } from '@tmagic/design';
20
- import type { FormValue } from '@tmagic/form';
21
+ import type { FormState, FormValue } from '@tmagic/form';
21
22
  import { MForm } from '@tmagic/form';
22
23
 
23
24
  import type { Services } from '@editor/type';
@@ -26,6 +27,10 @@ defineOptions({
26
27
  name: 'MEditorPropsPanel',
27
28
  });
28
29
 
30
+ defineProps<{
31
+ extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
32
+ }>();
33
+
29
34
  const emit = defineEmits(['mounted']);
30
35
 
31
36
  const internalInstance = getCurrentInstance();
@@ -78,5 +83,5 @@ const submit = async () => {
78
83
  }
79
84
  };
80
85
 
81
- defineExpose({ submit });
86
+ defineExpose({ configForm, submit });
82
87
  </script>
@@ -18,11 +18,9 @@
18
18
  @drag="dragHandler"
19
19
  >
20
20
  <slot name="component-list-item" :component="item">
21
- <MIcon :icon="item.icon"></MIcon>
21
+ <MIcon :title="item.text" :icon="item.icon"></MIcon>
22
22
 
23
- <TMagicTooltip effect="dark" placement="bottom" :content="item.text">
24
- <span>{{ item.text }}</span>
25
- </TMagicTooltip>
23
+ <span :title="item.text">{{ item.text }}</span>
26
24
  </slot>
27
25
  </div>
28
26
  </TMagicCollapseItem>
@@ -36,7 +34,7 @@ import { computed, inject, ref } from 'vue';
36
34
  import { Grid } from '@element-plus/icons-vue';
37
35
  import serialize from 'serialize-javascript';
38
36
 
39
- import { TMagicCollapse, TMagicCollapseItem, TMagicScrollbar, TMagicTooltip } from '@tmagic/design';
37
+ import { TMagicCollapse, TMagicCollapseItem, TMagicScrollbar } from '@tmagic/design';
40
38
  import { removeClassNameByClassName } from '@tmagic/utils';
41
39
 
42
40
  import MIcon from '@editor/components/Icon.vue';
@@ -83,14 +81,13 @@ const appendComponent = ({ text, type, data = {} }: ComponentItem): void => {
83
81
 
84
82
  const dragstartHandler = ({ text, type, data = {} }: ComponentItem, e: DragEvent) => {
85
83
  if (e.dataTransfer) {
86
- e.dataTransfer.effectAllowed = 'move';
87
84
  e.dataTransfer.setData(
88
- 'data',
85
+ 'text/json',
89
86
  serialize({
90
87
  name: text,
91
88
  type,
92
89
  ...data,
93
- }).replace(/"(\w+)":\s/g, '$1: '),
90
+ }),
94
91
  );
95
92
  }
96
93
  };
@@ -121,6 +118,6 @@ const dragHandler = (e: DragEvent) => {
121
118
 
122
119
  if (timeout || !stage.value) return;
123
120
 
124
- timeout = stage.value.getAddContainerHighlightClassNameTimeout(e);
121
+ timeout = stage.value.delayedMarkContainer(e);
125
122
  };
126
123
  </script>
@@ -4,12 +4,14 @@
4
4
 
5
5
  <script lang="ts" setup>
6
6
  import { computed, inject, markRaw, ref } from 'vue';
7
- import { CopyDocument, Delete, Files, Plus } from '@element-plus/icons-vue';
7
+ import { Files, Plus } from '@element-plus/icons-vue';
8
8
 
9
- import { NodeType } from '@tmagic/schema';
9
+ import { isPage } from '@tmagic/utils';
10
10
 
11
11
  import ContentMenu from '@editor/components/ContentMenu.vue';
12
+ import FolderMinusIcon from '@editor/icons/FolderMinusIcon.vue';
12
13
  import type { ComponentGroup, MenuButton, MenuComponent, Services } from '@editor/type';
14
+ import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
13
15
 
14
16
  defineOptions({
15
17
  name: 'MEditorLayerMenu',
@@ -19,12 +21,14 @@ const props = defineProps<{
19
21
  layerContentMenu: (MenuButton | MenuComponent)[];
20
22
  }>();
21
23
 
24
+ const emit = defineEmits<{
25
+ 'collapse-all': [];
26
+ }>();
27
+
22
28
  const services = inject<Services>('services');
23
29
  const menu = ref<InstanceType<typeof ContentMenu>>();
24
30
  const node = computed(() => services?.editorService.get('node'));
25
31
  const nodes = computed(() => services?.editorService.get('nodes'));
26
- const isRoot = computed(() => node.value?.type === NodeType.ROOT);
27
- const isPage = computed(() => node.value?.type === NodeType.PAGE);
28
32
  const componentList = computed(() => services?.componentListService.getList() || []);
29
33
 
30
34
  const createMenuItems = (group: ComponentGroup): MenuButton[] =>
@@ -81,29 +85,24 @@ const getSubMenuData = computed<MenuButton[]>(() => {
81
85
  const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
82
86
  {
83
87
  type: 'button',
84
- text: '新增',
85
- icon: markRaw(Plus),
86
- display: () => node.value?.items && nodes.value?.length === 1,
87
- items: getSubMenuData.value,
88
- },
89
- {
90
- type: 'button',
91
- text: '复制',
92
- icon: markRaw(CopyDocument),
93
- display: () => !isRoot.value,
88
+ text: '全部折叠',
89
+ icon: FolderMinusIcon,
90
+ display: () => isPage(node.value),
94
91
  handler: () => {
95
- node.value && services?.editorService.copy(nodes.value || []);
92
+ emit('collapse-all');
96
93
  },
97
94
  },
98
95
  {
99
96
  type: 'button',
100
- text: '删除',
101
- icon: markRaw(Delete),
102
- display: () => !isRoot.value && !isPage.value,
103
- handler: () => {
104
- node.value && services?.editorService.remove(nodes.value || []);
105
- },
97
+ text: '新增',
98
+ icon: markRaw(Plus),
99
+ display: () => node.value?.items && nodes.value?.length === 1,
100
+ items: getSubMenuData.value,
106
101
  },
102
+ useCopyMenu(),
103
+ usePasteMenu(),
104
+ useDeleteMenu(),
105
+ useMoveToMenu(services),
107
106
  ...props.layerContentMenu,
108
107
  ]);
109
108