@tmagic/editor 1.3.0-beta.0 → 1.3.0-beta.2

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 (46) hide show
  1. package/dist/style.css +30 -2
  2. package/dist/tmagic-editor.js +1053 -647
  3. package/dist/tmagic-editor.js.map +1 -1
  4. package/dist/tmagic-editor.umd.cjs +1071 -665
  5. package/dist/tmagic-editor.umd.cjs.map +1 -1
  6. package/package.json +10 -10
  7. package/src/components/CodeBlockEditor.vue +15 -6
  8. package/src/components/ContentMenu.vue +88 -42
  9. package/src/components/Resizer.vue +8 -30
  10. package/src/components/SplitView.vue +3 -2
  11. package/src/components/ToolButton.vue +1 -1
  12. package/src/fields/DataSourceFields.vue +10 -0
  13. package/src/fields/DataSourceMethods.vue +1 -0
  14. package/src/fields/EventSelect.vue +15 -3
  15. package/src/hooks/use-getso.ts +35 -0
  16. package/src/icons/PinIcon.vue +25 -0
  17. package/src/icons/PinnedIcon.vue +25 -0
  18. package/src/index.ts +2 -0
  19. package/src/layouts/workspace/Workspace.vue +1 -1
  20. package/src/layouts/workspace/viewer/NodeListMenu.vue +166 -0
  21. package/src/layouts/workspace/viewer/NodeListMenuTitle.vue +68 -0
  22. package/src/layouts/workspace/{Stage.vue → viewer/Stage.vue} +2 -0
  23. package/src/layouts/workspace/{ViewerMenu.vue → viewer/ViewerMenu.vue} +1 -1
  24. package/src/services/dataSource.ts +11 -0
  25. package/src/theme/content-menu.scss +29 -3
  26. package/src/theme/index.scss +8 -0
  27. package/src/type.ts +3 -0
  28. package/src/utils/data-source/formConfigs/http.ts +9 -0
  29. package/src/utils/data-source/index.ts +12 -0
  30. package/src/utils/props.ts +21 -10
  31. package/types/components/CodeBlockEditor.vue.d.ts +2 -0
  32. package/types/components/ContentMenu.vue.d.ts +27 -8
  33. package/types/components/Resizer.vue.d.ts +1 -3
  34. package/types/hooks/use-code-block-edit.d.ts +7 -0
  35. package/types/hooks/use-data-source-method.d.ts +7 -0
  36. package/types/hooks/use-getso.d.ts +5 -0
  37. package/types/icons/PinIcon.vue.d.ts +2 -0
  38. package/types/icons/PinnedIcon.vue.d.ts +2 -0
  39. package/types/index.d.ts +1 -0
  40. package/types/layouts/PropsPanel.vue.d.ts +1 -1
  41. package/types/layouts/workspace/viewer/NodeListMenu.vue.d.ts +15 -0
  42. package/types/layouts/workspace/viewer/NodeListMenuTitle.vue.d.ts +15 -0
  43. package/types/layouts/workspace/{Stage.vue.d.ts → viewer/Stage.vue.d.ts} +1 -1
  44. package/types/layouts/workspace/{ViewerMenu.vue.d.ts → viewer/ViewerMenu.vue.d.ts} +1 -1
  45. package/types/services/dataSource.d.ts +4 -0
  46. package/types/type.d.ts +3 -0
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.0-beta.0",
2
+ "version": "1.3.0-beta.2",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -46,13 +46,13 @@
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-beta.0",
50
- "@tmagic/design": "1.3.0-beta.0",
51
- "@tmagic/form": "1.3.0-beta.0",
52
- "@tmagic/schema": "1.3.0-beta.0",
53
- "@tmagic/stage": "1.3.0-beta.0",
54
- "@tmagic/table": "1.3.0-beta.0",
55
- "@tmagic/utils": "1.3.0-beta.0",
49
+ "@tmagic/core": "1.3.0-beta.2",
50
+ "@tmagic/design": "1.3.0-beta.2",
51
+ "@tmagic/form": "1.3.0-beta.2",
52
+ "@tmagic/schema": "1.3.0-beta.2",
53
+ "@tmagic/stage": "1.3.0-beta.2",
54
+ "@tmagic/table": "1.3.0-beta.2",
55
+ "@tmagic/utils": "1.3.0-beta.2",
56
56
  "buffer": "^6.0.3",
57
57
  "color": "^3.1.3",
58
58
  "events": "^3.3.0",
@@ -64,8 +64,8 @@
64
64
  "vue": "^3.3.4"
65
65
  },
66
66
  "peerDependencies": {
67
- "@tmagic/design": "1.3.0-beta.0",
68
- "@tmagic/form": "1.3.0-beta.0",
67
+ "@tmagic/design": "1.3.0-beta.2",
68
+ "@tmagic/form": "1.3.0-beta.2",
69
69
  "monaco-editor": "^0.41.0",
70
70
  "vue": "^3.3.4"
71
71
  },
@@ -50,7 +50,7 @@
50
50
  import { computed, inject, onUnmounted, ref } from 'vue';
51
51
 
52
52
  import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design';
53
- import { ColumnConfig, FormState, MFormDrawer } from '@tmagic/form';
53
+ import { ColumnConfig, FormConfig, FormState, MFormDrawer } from '@tmagic/form';
54
54
  import type { CodeBlockContent } from '@tmagic/schema';
55
55
 
56
56
  import CodeEditor from '@editor/layouts/CodeEditor.vue';
@@ -65,6 +65,7 @@ const props = defineProps<{
65
65
  content: CodeBlockContent;
66
66
  disabled?: boolean;
67
67
  isDataSource?: boolean;
68
+ dataSourceType?: string;
68
69
  }>();
69
70
 
70
71
  const emit = defineEmits<{
@@ -135,10 +136,11 @@ const defaultParamColConfig: ColumnConfig = {
135
136
  ],
136
137
  };
137
138
 
138
- const functionConfig = computed(() => [
139
+ const functionConfig = computed<FormConfig>(() => [
139
140
  {
140
141
  text: '名称',
141
142
  name: 'name',
143
+ rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],
142
144
  },
143
145
  {
144
146
  text: '描述',
@@ -148,10 +150,17 @@ const functionConfig = computed(() => [
148
150
  text: '执行时机',
149
151
  name: 'timing',
150
152
  type: 'select',
151
- options: [
152
- { text: '初始化前', value: 'beforeInit' },
153
- { text: '初始化后', value: 'afterInit' },
154
- ],
153
+ options: () => {
154
+ const options = [
155
+ { text: '初始化前', value: 'beforeInit' },
156
+ { text: '初始化后', value: 'afterInit' },
157
+ ];
158
+ if (props.dataSourceType !== 'base') {
159
+ options.push({ text: '请求前', value: 'beforeRequest' });
160
+ options.push({ text: '请求后', value: 'afterRequest' });
161
+ }
162
+ return options;
163
+ },
155
164
  display: () => props.isDataSource,
156
165
  },
157
166
  {
@@ -1,31 +1,42 @@
1
1
  <template>
2
- <div v-if="menuData.length" v-show="visible" class="magic-editor-content-menu" ref="menu" :style="menuStyle">
3
- <div>
4
- <ToolButton
5
- v-for="(item, index) in menuData"
6
- event-type="mouseup"
7
- ref="buttons"
8
- :data="item"
9
- :key="index"
10
- @mouseup="hide"
11
- @mouseenter="showSubMenu(item, index)"
12
- ></ToolButton>
2
+ <transition name="fade">
3
+ <div
4
+ v-show="visible"
5
+ class="magic-editor-content-menu"
6
+ ref="menu"
7
+ :style="menuStyle"
8
+ @mouseenter="mouseenterHandler()"
9
+ >
10
+ <slot name="title"></slot>
11
+ <div>
12
+ <ToolButton
13
+ v-for="(item, index) in menuData"
14
+ event-type="mouseup"
15
+ ref="buttons"
16
+ :class="{ active: active && item.id === active }"
17
+ :data="item"
18
+ :key="index"
19
+ @mouseup="clickHandler"
20
+ @mouseenter="showSubMenu(item, index)"
21
+ ></ToolButton>
22
+ </div>
23
+ <teleport to="body">
24
+ <content-menu
25
+ v-if="subMenuData.length"
26
+ class="sub-menu"
27
+ ref="subMenu"
28
+ :active="active"
29
+ :menu-data="subMenuData"
30
+ :is-sub-menu="true"
31
+ @hide="hide"
32
+ ></content-menu>
33
+ </teleport>
13
34
  </div>
14
- <teleport to="body">
15
- <content-menu
16
- v-if="subMenuData.length"
17
- class="sub-menu"
18
- ref="subMenu"
19
- :menu-data="subMenuData"
20
- :is-sub-menu="true"
21
- @hide="hide"
22
- ></content-menu>
23
- </teleport>
24
- </div>
35
+ </transition>
25
36
  </template>
26
37
 
27
38
  <script lang="ts" setup>
28
- import { nextTick, onMounted, onUnmounted, ref } from 'vue';
39
+ import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
29
40
 
30
41
  import { MenuButton, MenuComponent } from '@editor/type';
31
42
 
@@ -39,25 +50,38 @@ const props = withDefaults(
39
50
  defineProps<{
40
51
  menuData?: (MenuButton | MenuComponent)[];
41
52
  isSubMenu?: boolean;
53
+ active?: string | number;
54
+ autoHide?: boolean;
42
55
  }>(),
43
56
  {
44
57
  menuData: () => [],
45
58
  isSubMenu: false,
59
+ autoHide: true,
46
60
  },
47
61
  );
48
62
 
49
- const emit = defineEmits(['hide', 'show']);
63
+ const emit = defineEmits<{
64
+ hide: [];
65
+ show: [];
66
+ mouseenter: [];
67
+ }>();
50
68
 
51
69
  const menu = ref<HTMLDivElement>();
52
70
  const buttons = ref<InstanceType<typeof ToolButton>[]>();
53
71
  const subMenu = ref<any>();
54
72
  const visible = ref(false);
55
73
  const subMenuData = ref<(MenuButton | MenuComponent)[]>([]);
56
- const menuStyle = ref({
57
- left: '0',
58
- top: '0',
74
+
75
+ const menuPosition = ref({
76
+ left: 0,
77
+ top: 0,
59
78
  });
60
79
 
80
+ const menuStyle = computed(() => ({
81
+ top: `${menuPosition.value.top}px`,
82
+ left: `${menuPosition.value.left}px`,
83
+ }));
84
+
61
85
  const contains = (el: HTMLElement) => menu.value?.contains(el) || subMenu.value?.contains(el);
62
86
 
63
87
  const hide = () => {
@@ -69,7 +93,15 @@ const hide = () => {
69
93
  emit('hide');
70
94
  };
71
95
 
72
- const hideHandler = (e: MouseEvent) => {
96
+ const clickHandler = () => {
97
+ if (!props.autoHide) return;
98
+
99
+ hide();
100
+ };
101
+
102
+ const outsideClickhideHandler = (e: MouseEvent) => {
103
+ if (!props.autoHide) return;
104
+
73
105
  const target = e.target as HTMLElement | undefined;
74
106
  if (!visible.value || !target) {
75
107
  return;
@@ -80,23 +112,31 @@ const hideHandler = (e: MouseEvent) => {
80
112
  hide();
81
113
  };
82
114
 
83
- const show = (e: MouseEvent) => {
115
+ const setPosition = (e: { clientY: number; clientX: number }) => {
116
+ const menuHeight = menu.value?.clientHeight || 0;
117
+
118
+ let top = e.clientY;
119
+ if (menuHeight + e.clientY > document.body.clientHeight) {
120
+ top = document.body.clientHeight - menuHeight;
121
+ }
122
+
123
+ menuPosition.value = {
124
+ top,
125
+ left: e.clientX,
126
+ };
127
+ };
128
+
129
+ const show = (e?: MouseEvent) => {
84
130
  // 加settimeout是以为,如果菜单中的按钮监听的是mouseup,那么菜单显示出来时鼠标如果正好在菜单上就会马上触发按钮的mouseup
85
131
  setTimeout(() => {
86
132
  visible.value = true;
87
133
 
88
- nextTick(() => {
89
- const menuHeight = menu.value?.clientHeight || 0;
90
-
91
- let top = e.clientY;
92
- if (menuHeight + e.clientY > document.body.clientHeight) {
93
- top = document.body.clientHeight - menuHeight;
94
- }
134
+ if (!e) {
135
+ return;
136
+ }
95
137
 
96
- menuStyle.value = {
97
- top: `${top}px`,
98
- left: `${e.clientX}px`,
99
- };
138
+ nextTick(() => {
139
+ setPosition(e);
100
140
 
101
141
  emit('show');
102
142
  });
@@ -126,22 +166,28 @@ const showSubMenu = (item: MenuButton | MenuComponent, index: number) => {
126
166
  }, 0);
127
167
  };
128
168
 
169
+ const mouseenterHandler = () => {
170
+ emit('mouseenter');
171
+ };
172
+
129
173
  onMounted(() => {
130
174
  if (props.isSubMenu) return;
131
175
 
132
- globalThis.addEventListener('mousedown', hideHandler, true);
176
+ globalThis.addEventListener('mousedown', outsideClickhideHandler, true);
133
177
  });
134
178
 
135
179
  onUnmounted(() => {
136
180
  if (props.isSubMenu) return;
137
181
 
138
- globalThis.removeEventListener('mousedown', hideHandler, true);
182
+ globalThis.removeEventListener('mousedown', outsideClickhideHandler, true);
139
183
  });
140
184
 
141
185
  defineExpose({
142
186
  menu,
187
+ menuPosition,
143
188
  hide,
144
189
  show,
145
190
  contains,
191
+ setPosition,
146
192
  });
147
193
  </script>
@@ -5,41 +5,19 @@
5
5
  </template>
6
6
 
7
7
  <script lang="ts" setup>
8
- import { onMounted, onUnmounted, ref } from 'vue';
9
- import Gesto from 'gesto';
8
+ import { ref } from 'vue';
9
+ import type { OnDrag } from 'gesto';
10
+
11
+ import { useGetSo } from '@editor/hooks/use-getso';
10
12
 
11
13
  defineOptions({
12
14
  name: 'MEditorResizer',
13
15
  });
14
16
 
15
- const emit = defineEmits(['change']);
17
+ const emit = defineEmits<{
18
+ change: [e: OnDrag];
19
+ }>();
16
20
 
17
21
  const target = ref<HTMLSpanElement>();
18
- const isDraging = ref(false);
19
-
20
- let getso: Gesto;
21
-
22
- onMounted(() => {
23
- if (!target.value) return;
24
- getso = new Gesto(target.value, {
25
- container: window,
26
- pinchOutside: true,
27
- })
28
- .on('drag', (e) => {
29
- if (!target.value) return;
30
-
31
- emit('change', e.deltaX);
32
- })
33
- .on('dragStart', () => {
34
- isDraging.value = true;
35
- })
36
- .on('dragEnd', () => {
37
- isDraging.value = false;
38
- });
39
- });
40
-
41
- onUnmounted(() => {
42
- getso?.unset();
43
- isDraging.value = false;
44
- });
22
+ const { isDraging } = useGetSo(target, emit);
45
23
  </script>
@@ -22,6 +22,7 @@
22
22
 
23
23
  <script lang="ts" setup>
24
24
  import { computed, onMounted, onUnmounted, ref } from 'vue';
25
+ import { OnDrag } from 'gesto';
25
26
 
26
27
  import Resizer from './Resizer.vue';
27
28
 
@@ -111,7 +112,7 @@ onUnmounted(() => {
111
112
 
112
113
  const center = ref(0);
113
114
 
114
- const changeLeft = (deltaX: number) => {
115
+ const changeLeft = ({ deltaX }: OnDrag) => {
115
116
  if (typeof props.left === 'undefined') return;
116
117
  let left = Math.max(props.left + deltaX, props.minLeft) || 0;
117
118
  emit('update:left', left);
@@ -131,7 +132,7 @@ const changeLeft = (deltaX: number) => {
131
132
  });
132
133
  };
133
134
 
134
- const changeRight = (deltaX: number) => {
135
+ const changeRight = ({ deltaX }: OnDrag) => {
135
136
  if (typeof props.right === 'undefined') return;
136
137
  let right = Math.max(props.right - deltaX, props.minRight) || 0;
137
138
  emit('update:right', right);
@@ -16,7 +16,7 @@
16
16
  ><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></TMagicButton
17
17
  >
18
18
  </TMagicTooltip>
19
- <TMagicButton v-else size="small" text :disabled="disabled"
19
+ <TMagicButton v-else size="small" text :disabled="disabled" :title="data.text"
20
20
  ><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></TMagicButton
21
21
  >
22
22
  </template>
@@ -171,6 +171,16 @@ const dataSourceFieldsConfig: FormConfig = [
171
171
  {
172
172
  name: 'defaultValue',
173
173
  text: '默认值',
174
+ type: (mForm: FormState | undefined, { model }: any) => {
175
+ if (model.type === 'number') return 'number';
176
+ if (model.type === 'boolean') return 'select';
177
+
178
+ return 'text';
179
+ },
180
+ options: [
181
+ { text: 'true', value: true },
182
+ { text: 'false', value: false },
183
+ ],
174
184
  },
175
185
  {
176
186
  name: 'enable',
@@ -14,6 +14,7 @@
14
14
  :disabled="disabled"
15
15
  :content="codeConfig"
16
16
  :is-data-source="true"
17
+ :data-source-type="model.type"
17
18
  @submit="submitCodeHandler"
18
19
  ></CodeBlockEditor>
19
20
  </div>
@@ -52,6 +52,7 @@ import { computed, inject } from 'vue';
52
52
  import { Delete } from '@element-plus/icons-vue';
53
53
  import { has } from 'lodash-es';
54
54
 
55
+ import type { EventOption } from '@tmagic/core';
55
56
  import { TMagicButton } from '@tmagic/design';
56
57
  import type { FieldProps, FormState } from '@tmagic/form';
57
58
  import { ActionType } from '@tmagic/schema';
@@ -75,11 +76,22 @@ const eventNameConfig = computed(() => {
75
76
  text: '事件',
76
77
  type: 'select',
77
78
  labelWidth: '40px',
78
- options: (mForm: FormState, { formValue }: any) =>
79
- services?.eventsService.getEvent(formValue.type).map((option: any) => ({
79
+ options: (mForm: FormState, { formValue }: any) => {
80
+ let events: EventOption[] = [];
81
+
82
+ if (!services) return events;
83
+
84
+ if (props.config.src === 'component') {
85
+ events = services.eventsService.getEvent(formValue.type);
86
+ } else if (props.config.src === 'datasource') {
87
+ events = services.dataSourceService.getFormEvent(formValue.type);
88
+ }
89
+
90
+ return events.map((option) => ({
80
91
  text: option.label,
81
92
  value: option.value,
82
- })),
93
+ }));
94
+ },
83
95
  };
84
96
  return { ...defaultEventNameConfig, ...props.config.eventNameConfig };
85
97
  });
@@ -0,0 +1,35 @@
1
+ import { onMounted, onUnmounted, type Ref, ref } from 'vue';
2
+ import Gesto, { type OnDrag } from 'gesto';
3
+
4
+ export const useGetSo = (target: Ref<HTMLElement | undefined>, emit: (evt: 'change', e: OnDrag<Gesto>) => void) => {
5
+ let getso: Gesto;
6
+ const isDraging = ref(false);
7
+
8
+ onMounted(() => {
9
+ if (!target.value) return;
10
+ getso = new Gesto(target.value, {
11
+ container: window,
12
+ pinchOutside: true,
13
+ })
14
+ .on('drag', (e) => {
15
+ if (!target.value) return;
16
+
17
+ emit('change', e);
18
+ })
19
+ .on('dragStart', () => {
20
+ isDraging.value = true;
21
+ })
22
+ .on('dragEnd', () => {
23
+ isDraging.value = false;
24
+ });
25
+ });
26
+
27
+ onUnmounted(() => {
28
+ getso?.unset();
29
+ isDraging.value = false;
30
+ });
31
+
32
+ return {
33
+ isDraging,
34
+ };
35
+ };
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ class="icon icon-tabler icon-tabler-pin"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ stroke-width="2"
9
+ stroke="currentColor"
10
+ fill="none"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
15
+ <path d="M15 4.5l-4 4l-4 1.5l-1.5 1.5l7 7l1.5 -1.5l1.5 -4l4 -4" />
16
+ <line x1="9" y1="15" x2="4.5" y2="19.5" />
17
+ <line x1="14.5" y1="4" x2="20" y2="9.5" />
18
+ </svg>
19
+ </template>
20
+
21
+ <script lang="ts" setup>
22
+ defineOptions({
23
+ name: 'MEditorPinIcon',
24
+ });
25
+ </script>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ class="icon icon-tabler icon-tabler-pinned"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ stroke-width="2"
9
+ stroke="currentColor"
10
+ fill="none"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
15
+ <path d="M9 4v6l-2 4v2h10v-2l-2 -4v-6" />
16
+ <line x1="12" y1="16" x2="12" y2="21" />
17
+ <line x1="8" y1="4" x2="16" y2="4" />
18
+ </svg>
19
+ </template>
20
+
21
+ <script lang="ts" setup>
22
+ defineOptions({
23
+ name: 'MEditorPinnedIcon',
24
+ });
25
+ </script>
package/src/index.ts CHANGED
@@ -37,6 +37,8 @@ import type { InstallOptions } from './type';
37
37
 
38
38
  import './theme/index.scss';
39
39
 
40
+ export type { OnDrag } from 'gesto';
41
+
40
42
  export type { MoveableOptions } from '@tmagic/stage';
41
43
  export * from './type';
42
44
  export * from './hooks';
@@ -20,9 +20,9 @@ import { computed, inject } from 'vue';
20
20
 
21
21
  import { MenuButton, MenuComponent, Services } from '@editor/type';
22
22
 
23
+ import MagicStage from './viewer/Stage.vue';
23
24
  import Breadcrumb from './Breadcrumb.vue';
24
25
  import PageBar from './PageBar.vue';
25
- import MagicStage from './Stage.vue';
26
26
 
27
27
  defineOptions({
28
28
  name: 'MEditorWorkspace',