1
0
mirror of synced 2025-12-08 10:07:54 +08:00

Compare commits

...

8 Commits

Author SHA1 Message Date
roymondchen
6314078c10 v1.0.0-beta.9 2022-03-31 15:52:07 +08:00
roymondchen
b8f16eb3e1 fix(editor): 修复pagebar文字超长换行问题 2022-03-31 15:37:21 +08:00
roymondchen
4d2e42842f fix(stage): 修复画布缩放后拖动鼠标漂移 2022-03-31 15:37:21 +08:00
roymondchen
7b061ef8d3 feat(stage): 组件对齐不准确,暂时去掉 2022-03-31 15:37:21 +08:00
roymondchen
67eab4a131 feat(stage): 增加当前拖动的节点的z-index 2022-03-31 15:37:21 +08:00
roymondchen
af61b10775 refactor(stage): 优化拖动体验 2022-03-31 15:34:28 +08:00
roymondchen
464d713b99 fix(editor): 新增页面不用添加历史记录 2022-03-31 15:34:28 +08:00
roymondchen
a24b57a129 style(editor): 将节点类型改成枚举 2022-03-31 15:34:28 +08:00
33 changed files with 220 additions and 86 deletions

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"npmClient": "npm",
"packages": [
"packages/*",

28
package-lock.json generated
View File

@@ -15678,6 +15678,15 @@
"react-simple-compat": "^1.2.1"
}
},
"moveable-helper": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/moveable-helper/-/moveable-helper-0.4.0.tgz",
"integrity": "sha512-t1FK9PO187Gn0N6GVZcrQgePjiHmuj8eUhmJjH38LvTMnVVxiHzWYRx6ARFZvSFIIW4yb6BEAv4C99Bsx84nFw==",
"requires": {
"@daybrush/utils": "^1.0.0",
"scenejs": "^1.4.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -16420,6 +16429,14 @@
}
}
},
"order-map": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/order-map/-/order-map-0.2.2.tgz",
"integrity": "sha512-X//Db/lT11tdxxutWQfE+bmbTyieDJWrr/vSiwBwOf8RRw9yAgF7gqn1ihFmfX2E7l7gcPcucep3aWFjo5FpoA==",
"requires": {
"@daybrush/utils": "^1.0.0"
}
},
"os-browserify": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
@@ -18067,6 +18084,17 @@
"xmlchars": "^2.1.1"
}
},
"scenejs": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/scenejs/-/scenejs-1.5.0.tgz",
"integrity": "sha512-Xo6LjUsaVcTbC+FGMMwUH1jthAQUj6bq2i55iEBBifrBn/nzm/++dGo8tqjCzMbm6KUPOpiry38N7r9QY2mWpQ==",
"requires": {
"@daybrush/utils": "^1.3.1",
"@scena/event-emitter": "^1.0.3",
"css-styled": "^1.0.0",
"order-map": "^0.2.2"
}
},
"schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"name": "@tmagic/core",
"sideEffects": false,
"main": "dist/tmagic-core.umd.js",
@@ -26,7 +26,7 @@
"url": "https://github.com/Tencent/tmagic-editor.git"
},
"dependencies": {
"@tmagic/schema": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.9",
"events": "^3.3.0"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@tmagic/editor",
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"name": "@tmagic/editor",
"sideEffects": false,
"main": "dist/tmagic-editor.umd.js",
@@ -28,10 +28,10 @@
},
"dependencies": {
"@element-plus/icons": "0.0.11",
"@tmagic/core": "^1.0.0-beta.8",
"@tmagic/core": "^1.0.0-beta.9",
"@tmagic/form": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.8",
"@tmagic/stage": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.9",
"@tmagic/stage": "^1.0.0-beta.9",
"@tmagic/utils": "^1.0.0-beta.8",
"color": "^3.1.3",
"element-plus": "^2.1.7",

View File

@@ -41,6 +41,8 @@
import { computed, defineComponent, inject, PropType } from 'vue';
import { ArrowDown, Back, Delete, Grid, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons';
import { NodeType } from '@tmagic/schema';
import MIcon from '@editor/components/Icon.vue';
import type { MenuButton, MenuComponent, MenuItem, Services } from '@editor/type';
@@ -87,7 +89,7 @@ export default defineComponent({
type: 'button',
icon: Delete,
tooltip: '刪除',
disabled: () => services?.editorService.get('node')?.type === 'page',
disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE,
handler: () => services?.editorService.remove(services?.editorService.get('node')),
};
case 'undo':

View File

@@ -15,6 +15,8 @@
import { defineComponent, inject, toRaw } from 'vue';
import { Plus } from '@element-plus/icons';
import { NodeType } from '@tmagic/schema';
import { Services } from '@editor/type';
import { generatePageNameByApp } from '@editor/utils';
@@ -31,7 +33,7 @@ export default defineComponent({
if (!editorService) return;
editorService.add({
type: 'page',
type: NodeType.PAGE,
name: generatePageNameByApp(toRaw(editorService.get('root'))),
});
},

View File

@@ -58,6 +58,7 @@ import type { ElTree } from 'element-plus';
import { throttle } from 'lodash-es';
import type { MNode, MPage } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
import type { EditorService } from '@editor/services/editor';
import type { Services } from '@editor/type';
@@ -88,8 +89,8 @@ const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorServi
const { type: ingType } = ingData;
if (ingType !== 'page' && data.type === 'page') return false;
if (ingType === 'page' && data.type !== 'page') return false;
if (ingType !== NodeType.PAGE && data.type === NodeType.PAGE) return false;
if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
if (!data || !data.type) return false;
if (['prev', 'next'].includes(type)) return true;
if (data.items || data.type === 'container') return true;

View File

@@ -11,9 +11,13 @@
:class="{ active: page?.id === item.id }"
@click="switchPage(item)"
>
<slot name="page-bar-title" :page="item">
<span>{{ item.name }}</span>
</slot>
<div class="m-editor-page-bar-title">
<slot name="page-bar-title" :page="item">
<el-tooltip effect="dark" placement="top-start" :content="item.name">
<span>{{ item.name }}</span>
</el-tooltip>
</slot>
</div>
<el-popover placement="top" :width="160" trigger="hover">
<div>
@@ -38,6 +42,7 @@ import { computed, defineComponent, inject, toRaw } from 'vue';
import { CaretBottom, Plus } from '@element-plus/icons';
import type { MPage } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
import type { Services } from '@editor/type';
import { generatePageNameByApp } from '@editor/utils/editor';
@@ -60,7 +65,7 @@ export default defineComponent({
addPage() {
if (!editorService) return;
const pageConfig = {
type: 'page',
type: NodeType.PAGE,
name: generatePageNameByApp(toRaw(editorService.get('root'))),
};
editorService.add(pageConfig);

View File

@@ -24,6 +24,7 @@
<script lang="ts">
import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue';
import { NodeType } from '@tmagic/schema';
import type StageCore from '@tmagic/stage';
import { LayerOffset, Layout, Services } from '@editor/type';
@@ -53,7 +54,8 @@ export default defineComponent({
if (!parent.value || !editorService) return (canCenter.value = false);
const layout = await editorService.getLayout(parent.value);
canCenter.value =
[Layout.ABSOLUTE, Layout.FIXED].includes(layout) && !['app', 'page', 'pop'].includes(`${node.value?.type}`);
[Layout.ABSOLUTE, Layout.FIXED].includes(layout) &&
![NodeType.ROOT, NodeType.PAGE, 'pop'].includes(`${node.value?.type}`);
},
{ immediate: true },
);
@@ -62,8 +64,8 @@ export default defineComponent({
menu,
canPaste,
canDelete: computed(() => node.value?.type !== 'page'),
canMoveZPos: computed(() => node.value?.type !== 'page'),
canDelete: computed(() => node.value?.type !== NodeType.PAGE),
canMoveZPos: computed(() => node.value?.type !== NodeType.PAGE),
canCenter,
center() {

View File

@@ -21,6 +21,7 @@ import { cloneDeep, mergeWith } from 'lodash-es';
import serialize from 'serialize-javascript';
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
import type StageCore from '@tmagic/stage';
import { getNodePath, isPop } from '@tmagic/utils';
@@ -121,7 +122,7 @@ class Editor extends BaseService {
info.parent = path[path.length - 2] as MContainer;
path.forEach((item) => {
if (item.type === 'page') {
if (item.type === NodeType.PAGE) {
info.page = item as MPage;
return;
}
@@ -209,7 +210,7 @@ class Editor extends BaseService {
let parentNode: MNode | undefined;
if (type === 'page') {
if (type === NodeType.PAGE) {
parentNode = this.get<MApp>('root');
// 由于支持中间件扩展在parent参数为undefined时parent会变成next函数
} else if (parent && typeof parent !== 'function') {
@@ -225,7 +226,7 @@ class Editor extends BaseService {
const layout = await this.getLayout(parentNode);
const newNode = initPosition({ ...toRaw(await propsService.getPropsValue(type)), ...config }, layout);
if ((parentNode?.type === 'app' || curNode.type === 'app') && newNode.type !== 'page') {
if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
throw new Error('app下不能添加组件');
}
@@ -236,7 +237,9 @@ class Editor extends BaseService {
await this.select(newNode);
this.addModifiedNodeId(newNode.id);
this.pushHistoryState();
if (type !== NodeType.PAGE) {
this.pushHistoryState();
}
return newNode;
}
@@ -264,7 +267,7 @@ class Editor extends BaseService {
parent.items?.splice(index, 1);
this.get<StageCore>('stage')?.remove({ id: node.id, root: this.get('root') });
if (node.type === 'page') {
if (node.type === NodeType.PAGE) {
await this.select(root.items[0] || root);
} else {
await this.select(parent);
@@ -300,7 +303,7 @@ class Editor extends BaseService {
if (!newConfig.type) throw new Error('配置缺少type值');
if (newConfig.type === 'app') {
if (newConfig.type === NodeType.ROOT) {
this.set('root', newConfig);
return newConfig;
}
@@ -327,7 +330,7 @@ class Editor extends BaseService {
this.get<StageCore>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
if (newConfig.type === 'page') {
if (newConfig.type === NodeType.PAGE) {
this.set('page', newConfig);
}

View File

@@ -10,6 +10,7 @@
color: $--font-color;
border-top: 1px solid $--border-color;
z-index: 2;
overflow: hidden;
.m-editor-page-bar-item {
padding: 0 10px;
@@ -27,5 +28,12 @@
cursor: pointer;
}
}
.m-editor-page-bar-title {
max-width: 150px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
}

View File

@@ -18,7 +18,8 @@
import { random } from 'lodash-es';
import { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
import { getNodePath, isPop } from '@tmagic/utils';
import { Layout } from '@editor/type';
@@ -34,7 +35,7 @@ export const generateId = (type: string | number): string => `${type}_${random(1
*/
export const getPageList = (app: MApp): MPage[] => {
if (app.items && Array.isArray(app.items)) {
return app.items.filter((item: MPage) => item.type === 'page');
return app.items.filter((item: MPage) => item.type === NodeType.PAGE);
}
return [];
};
@@ -108,7 +109,7 @@ export const setNewItemId = (config: MNode, parent?: MPage) => {
config.name = `${config.name?.replace(/_(\d+)$/, '')}_${config.id}`;
// 只有弹窗在页面下的一级子元素才有效
if (isPop(config) && parent?.type === 'page') {
if (isPop(config) && parent?.type === NodeType.PAGE) {
updatePopId(oldId, config.id, parent);
}

View File

@@ -20,6 +20,7 @@ import { mount } from '@vue/test-utils';
import ElementPlus from 'element-plus';
import MagicForm from '@tmagic/form';
import { NodeType } from '@tmagic/schema';
import Editor from '@editor/Editor.vue';
@@ -47,14 +48,14 @@ describe('编辑器', () => {
},
props: {
modelValue: {
type: 'app',
type: NodeType.ROOT,
id: 1,
name: 'app',
items: [
{
type: 'page',
type: NodeType.PAGE,
id: 2,
name: 'page',
name: NodeType.PAGE,
items: [],
},
],

View File

@@ -19,13 +19,15 @@
import { mount } from '@vue/test-utils';
import ElementPlus from 'element-plus';
import { NodeType } from '@tmagic/schema';
import PageBar from '@editor/layouts/workspace/PageBar.vue';
const editorState: Record<string, any> = {
root: {
items: [{ key: 0, id: 1, name: 'testName', type: 'page' }],
items: [{ key: 0, id: 1, name: 'testName', type: NodeType.PAGE }],
},
page: { id: 1, type: 'page' },
page: { id: 1, type: NodeType.PAGE },
};
const editorService = {
@@ -54,7 +56,7 @@ describe('PageBar', () => {
await wrapper.find('i[class="el-icon m-editor-page-bar-menu-add-icon"]').trigger('click');
expect(editorService.add.mock.calls[0][0]).toEqual({
type: 'page',
type: NodeType.PAGE,
name: 'page_1',
});
done();

View File

@@ -18,6 +18,8 @@
import { mount } from '@vue/test-utils';
import { NodeType } from '@tmagic/schema';
import Stage from '@editor/layouts/workspace/Stage.vue';
globalThis.ResizeObserver =
@@ -40,7 +42,7 @@ describe('Stage.vue', () => {
);
const page = {
type: 'page',
type: NodeType.PAGE,
id: '2',
items: [
{
@@ -55,7 +57,7 @@ describe('Stage.vue', () => {
runtimeUrl: '',
root: {
id: '1',
type: 'app',
type: NodeType.ROOT,
items: [page],
},

View File

@@ -18,7 +18,8 @@
import { cloneDeep } from 'lodash-es';
import { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
import type { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
import editorService from '@editor/services/editor';
import { COPY_STORAGE_KEY } from '@editor/utils';
@@ -72,12 +73,12 @@ enum NodeId {
// mock 页面数据,包含一个页面,两个组件
const root: MNode = {
id: NodeId.ROOT_ID,
type: 'app',
type: NodeType.ROOT,
items: [
{
id: NodeId.PAGE_ID,
layout: 'absolute',
type: 'page',
type: NodeType.PAGE,
style: {
width: 375,
},
@@ -237,7 +238,7 @@ describe('add', () => {
const rootNode = editorService.get<MApp>('root');
const newNode = await editorService.add(
{
type: 'page',
type: NodeType.PAGE,
},
rootNode,
);
@@ -279,7 +280,7 @@ describe('remove', () => {
// 先加一个页面
const newPage = await editorService.add(
{
type: 'page',
type: NodeType.PAGE,
},
rootNode,
);
@@ -395,7 +396,7 @@ describe('alignCenter', () => {
it('正常', async () => {
// 设置当前编辑的页面
await editorService.select(NodeId.PAGE_ID);
await editorService.update({ id: NodeId.PAGE_ID, isAbsoluteLayout: true, type: 'page' });
await editorService.update({ id: NodeId.PAGE_ID, isAbsoluteLayout: true, type: NodeType.PAGE });
await editorService.select(NodeId.NODE_ID);
const node = editorService.get<MNode>('node');
await editorService.alignCenter(node);

View File

@@ -16,7 +16,8 @@
* limitations under the License.
*/
import { MNode } from '@tmagic/schema';
import type { MNode } from '@tmagic/schema';
import { NodeType } from '@tmagic/schema';
import * as editor from '@editor/utils/editor';
@@ -30,12 +31,12 @@ describe('util form', () => {
it('getPageList', () => {
const pageList = editor.getPageList({
id: 'app_1',
type: 'app',
type: NodeType.ROOT,
items: [
{
id: 'page_1',
name: 'index',
type: 'page',
type: NodeType.PAGE,
items: [],
},
],
@@ -49,7 +50,7 @@ describe('util form', () => {
{
id: 'page_1',
name: 'index',
type: 'page',
type: NodeType.PAGE,
items: [],
},
]);
@@ -79,7 +80,7 @@ describe('setNewItemId', () => {
it('items', () => {
const config = {
id: 1,
type: 'page',
type: NodeType.PAGE,
items: [
{
type: 'text',
@@ -95,7 +96,7 @@ describe('setNewItemId', () => {
it('pop', () => {
const config = {
id: 1,
type: 'page',
type: NodeType.PAGE,
items: [
{
type: 'button',
@@ -158,7 +159,7 @@ describe('getNodeIndex', () => {
},
{
id: 2,
type: 'page',
type: NodeType.PAGE,
items: [
{
type: 'text',
@@ -179,7 +180,7 @@ describe('getNodeIndex', () => {
},
{
id: 2,
type: 'page',
type: NodeType.PAGE,
items: [
{
type: 'text',

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"name": "@tmagic/schema",
"sideEffects": false,
"main": "src/index.ts",

View File

@@ -16,6 +16,12 @@
* limitations under the License.
*/
export enum NodeType {
CONTAINER = 'container',
PAGE = 'page',
ROOT = 'app',
}
export type Id = string | number;
export interface EventItemConfig {
@@ -47,19 +53,19 @@ export interface MComponent {
export interface MContainer extends MComponent {
/** 容器类型,默认为'container' */
type: 'container' | string;
type: NodeType.CONTAINER | string;
/** 容器子元素 */
items: (MComponent | MContainer)[];
}
export interface MPage extends MContainer {
/** 页面类型 */
type: 'page';
type: NodeType.PAGE;
}
export interface MApp extends MComponent {
/** App页面类型app作为整个结构的根节点有且只有一个 */
type: 'app';
type: NodeType.ROOT;
/** */
items: MPage[];
}

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"name": "@tmagic/stage",
"sideEffects": false,
"main": "dist/tmagic-stage.umd.js",
@@ -23,11 +23,12 @@
},
"dependencies": {
"@scena/guides": "^0.17.0",
"@tmagic/schema": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.9",
"@tmagic/utils": "^1.0.0-beta.8",
"events": "^3.3.0",
"lodash-es": "^4.17.21",
"moveable": "^0.28.0"
"moveable": "^0.28.0",
"moveable-helper": "^0.4.0"
},
"devDependencies": {
"@types/events": "^3.0.0",

View File

@@ -35,6 +35,7 @@ import {
UpdateData,
UpdateEventData,
} from './types';
import { addSelectedClassName, removeSelectedClassName } from './util';
export default class StageCore extends EventEmitter {
public selectedDom: Element | undefined;
@@ -46,6 +47,7 @@ export default class StageCore extends EventEmitter {
public highlightLayer: StageHighlight;
public config: StageCoreConfig;
public zoom = DEFAULT_ZOOM;
public container?: HTMLDivElement;
private canSelect: CanSelect;
constructor(config: StageCoreConfig) {
@@ -135,7 +137,8 @@ export default class StageCore extends EventEmitter {
if (el === this.selectedDom) return;
const runtime = await this.renderer?.getRuntime();
const runtime = await this.renderer.getRuntime();
if (runtime?.beforeSelect) {
await runtime.beforeSelect(el);
}
@@ -143,6 +146,13 @@ export default class StageCore extends EventEmitter {
this.mask.setLayout(el);
this.dr?.select(el, event);
this.selectedDom = el;
if (this.renderer.contentWindow) {
removeSelectedClassName(this.renderer.contentWindow.document);
if (this.selectedDom) {
addSelectedClassName(this.selectedDom);
}
}
}
/**
@@ -198,6 +208,7 @@ export default class StageCore extends EventEmitter {
* @param el 将stage挂载到该Dom节点上
*/
public mount(el: HTMLDivElement): void {
this.container = el;
const { mask, renderer } = this;
renderer.mount(el);
@@ -226,6 +237,8 @@ export default class StageCore extends EventEmitter {
highlightLayer.destroy();
this.removeAllListeners();
this.container = undefined;
}
private async getTargetElement(idOrEl: Id | HTMLElement): Promise<HTMLElement> {

View File

@@ -21,6 +21,7 @@ import { EventEmitter } from 'events';
import type { MoveableOptions } from 'moveable';
import Moveable from 'moveable';
import MoveableHelper from 'moveable-helper';
import { GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
import StageCore from './StageCore';
@@ -49,6 +50,7 @@ export default class StageDragResize extends EventEmitter {
private dragStatus: ActionStatus = ActionStatus.END;
private ghostEl: HTMLElement | undefined;
private mode: Mode = Mode.ABSOLUTE;
private moveableHelper?: MoveableHelper;
constructor(config: StageDragResizeConfig) {
super();
@@ -79,6 +81,12 @@ export default class StageDragResize extends EventEmitter {
target: this.dragEl || this.target,
});
this.moveableHelper = MoveableHelper.create({
useBeforeRender: true,
useRender: false,
createAuto: true,
});
this.initMoveable();
if (event) {
@@ -188,34 +196,42 @@ export default class StageDragResize extends EventEmitter {
private bindDragEvent(): void {
if (!this.moveable) throw new Error('moveable 为初始化');
let offset = {
left: 0,
top: 0,
};
this.moveable
.on('dragStart', () => {
.on('dragStart', (e) => {
if (!this.target) throw new Error('未选中组件');
this.dragStatus = ActionStatus.START;
this.moveableHelper?.onDragStart(e);
offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
if (this.mode === Mode.SORTABLE) {
this.ghostEl = this.generateGhostEl(this.target);
}
})
.on('drag', ({ left, top }) => {
.on('drag', (e) => {
if (!this.target || !this.dragEl) return;
this.dragStatus = ActionStatus.ING;
const offset = getAbsolutePosition(this.target, { left, top });
const { left, top } = e;
// 流式布局
if (this.ghostEl) {
this.dragEl.style.top = `${top}px`;
this.ghostEl.style.top = `${offset.top}px`;
this.ghostEl.style.top = `${top + offset.top}px`;
return;
}
this.dragEl.style.left = `${left}px`;
this.dragEl.style.top = `${top}px`;
this.moveableHelper?.onDrag(e);
this.target.style.left = `${offset.left}px`;
this.target.style.top = `${offset.top}px`;
this.target.style.left = `${left + offset.left}px`;
this.target.style.top = `${top + offset.top}px`;
})
.on('dragEnd', () => {
// 点击不拖动时会触发dragStart和dragEnd但是不会有drag事件
@@ -342,18 +358,26 @@ export default class StageDragResize extends EventEmitter {
}
return {
scrollable: true,
origin: true,
rootContainer: this.core.container,
zoom: 1,
dragArea: true,
dragArea: false,
draggable: true,
resizable: true,
snappable: isAbsolute,
snapGap: isAbsolute,
snapDirections: { center: isAbsolute, middle: isAbsolute },
elementSnapDirections: { center: isAbsolute, middle: isAbsolute },
elementGuidelines: isAbsolute ? await this.getSnapElements(this.target) : [],
snapThreshold: 5,
snapDigit: 0,
throttleDrag: 0,
isDisplaySnapDigit: isAbsolute,
snapDirections: {
top: isAbsolute,
right: isAbsolute,
bottom: isAbsolute,
left: isAbsolute,
center: isAbsolute,
middle: isAbsolute,
},
horizontalGuidelines: this.horizontalGuidelines,
verticalGuidelines: this.verticalGuidelines,

View File

@@ -18,6 +18,7 @@
import { EventEmitter } from 'events';
import { SELECTED_CLASS, ZIndex } from './const';
import StageCore from './StageCore';
import type { Runtime, RuntimeWindow, StageRenderConfig } from './types';
import { getHost, isSameDomain } from './util';
@@ -114,11 +115,23 @@ export default class StageRender extends EventEmitter {
private loadHandler = async () => {
this.emit('onload');
if (this.render) {
const el = await this.render(this.core);
if (el) {
this.iframe?.contentDocument?.body?.appendChild(el);
}
}
if (this.contentWindow) {
const style = this.contentWindow.document.createElement('style');
style.id = 'tmagic-stage-render';
style.innerHTML = `
.${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
z-index: ${ZIndex.SELECTED_EL};
}
`;
this.contentWindow.document.head.appendChild(style);
}
};
}

View File

@@ -29,6 +29,7 @@ export enum GuidesType {
export enum ZIndex {
MASK = '99999',
SELECTED_EL = '666',
}
export enum MouseButton {
@@ -42,3 +43,5 @@ export enum Mode {
FIXED = 'fixed',
SORTABLE = 'sortable',
}
export const SELECTED_CLASS = 'tmagic-stage-selected-area';

View File

@@ -16,7 +16,7 @@
* limitations under the License.
*/
import { Mode } from './const';
import { Mode, SELECTED_CLASS } from './const';
import type { Offset } from './types';
export const getOffset = (el: HTMLElement): Offset => {
@@ -154,3 +154,17 @@ export const createDiv = ({ className, cssText }: { className: string; cssText:
el.style.cssText = cssText;
return el;
};
export const removeSelectedClassName = (doc: Document) => {
const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
if (oldEl) {
oldEl.classList.remove(SELECTED_CLASS);
(oldEl.parentNode as HTMLDivElement)?.classList.remove(`${SELECTED_CLASS}-parent`);
}
};
export const addSelectedClassName = (el: Element) => {
el.classList.add(SELECTED_CLASS);
(el.parentNode as Element)?.classList.add(`${SELECTED_CLASS}-parent`);
};

View File

@@ -1,6 +1,6 @@
{
"name": "@tmagic/ui-react",
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@tmagic/ui-react",
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"main": "src/index.ts",
"engines": {
"node": ">=14"
@@ -13,7 +13,7 @@
"react:build": "tsc && vite build"
},
"dependencies": {
"@tmagic/schema": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.9",
"react": "^17.0.0",
"react-dom": "^17.0.0"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@tmagic/ui",
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"name": "@tmagic/ui",
"main": "src/index.ts",
"engines": {
@@ -10,7 +10,7 @@
"url": "https://github.com/Tencent/tmagic-editor.git"
},
"dependencies": {
"@tmagic/schema": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.9",
"delegate": "^3.2.0",
"tiny-emitter": "^2.1.0",
"vue": "^3.2.0"

View File

@@ -1,6 +1,6 @@
{
"name": "tmagic-playground",
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "tmagic-playground",
"version": "1.0.0-beta.8",
"version": "1.0.0-beta.9",
"private": true,
"scripts": {
"dev": "vite",
@@ -9,10 +9,10 @@
},
"dependencies": {
"@element-plus/icons": "0.0.11",
"@tmagic/editor": "^1.0.0-beta.8",
"@tmagic/editor": "^1.0.0-beta.9",
"@tmagic/form": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.8",
"@tmagic/stage": "^1.0.0-beta.8",
"@tmagic/schema": "^1.0.0-beta.9",
"@tmagic/stage": "^1.0.0-beta.9",
"@tmagic/utils": "^1.0.0-beta.8",
"element-plus": "^2.1.7",
"serialize-javascript": "^6.0.0",

View File

@@ -31,6 +31,7 @@ import { ElMessage } from 'element-plus';
import serialize from 'serialize-javascript';
import type { MenuBarData, MoveableOptions, TMagicEditor } from '@tmagic/editor';
import { NodeType } from '@tmagic/schema';
import StageCore from '@tmagic/stage';
import { asyncLoadJs } from '@tmagic/utils';
@@ -155,7 +156,7 @@ export default defineComponent({
if (!node) return options;
const isPage = node.type === 'page';
const isPage = node.type === NodeType.PAGE;
options.draggable = !isPage;
options.resizable = !isPage;