feat(stage,editor): 拖入指定容器支持配置成按住alt才开启

This commit is contained in:
roymondchen 2022-08-12 15:34:31 +08:00 committed by jia000
parent b1ce0be682
commit 4f8ea94ee8
7 changed files with 69 additions and 11 deletions

View File

@ -360,6 +360,22 @@ icon使用的是[element-plus icon](https://element-plus.org/zh-CN/component/ico
识别到容器后会给其dom上添加的class 识别到容器后会给其dom上添加的class
### containerHighlightType
- **类型:** 'default' | 'alt' | '';
- **默认值:** 'default'
- **详情:**
启动方式
default: 停留在画布上启动识别
alt: 按住alt键启动识别
其他值:不启动
## slots ## slots

View File

@ -30,7 +30,7 @@
<template #props-panel> <template #props-panel>
<slot name="props-panel"> <slot name="props-panel">
<props-panel ref="propsPanel" @mounted="(instance) => $emit('props-panel-mounted', instance)"> <props-panel ref="propsPanel" @mounted="(instance: any) => $emit('props-panel-mounted', instance)">
<template #props-panel-header> <template #props-panel-header>
<slot name="props-panel-header"></slot> <slot name="props-panel-header"></slot>
</template> </template>
@ -49,7 +49,7 @@ import { EventOption } from '@tmagic/core';
import type { FormConfig } from '@tmagic/form'; import type { FormConfig } from '@tmagic/form';
import type { MApp, MNode } from '@tmagic/schema'; import type { MApp, MNode } from '@tmagic/schema';
import type StageCore from '@tmagic/stage'; import type StageCore from '@tmagic/stage';
import { CONTAINER_HIGHLIGHT_CLASS, MoveableOptions } from '@tmagic/stage'; import { CONTAINER_HIGHLIGHT_CLASS, ContainerHighlightType, MoveableOptions } from '@tmagic/stage';
import Framework from './layouts/Framework.vue'; import Framework from './layouts/Framework.vue';
import NavMenu from './layouts/NavMenu.vue'; import NavMenu from './layouts/NavMenu.vue';
@ -170,6 +170,11 @@ export default defineComponent({
default: 800, default: 800,
}, },
containerHighlightType: {
type: String as PropType<ContainerHighlightType>,
default: ContainerHighlightType.DEFAULT,
},
stageRect: { stageRect: {
type: [String, Object] as PropType<StageRect>, type: [String, Object] as PropType<StageRect>,
}, },
@ -289,6 +294,7 @@ export default defineComponent({
isContainer: props.isContainer, isContainer: props.isContainer,
containerHighlightClassName: props.containerHighlightClassName, containerHighlightClassName: props.containerHighlightClassName,
containerHighlightDuration: props.containerHighlightDuration, containerHighlightDuration: props.containerHighlightDuration,
containerHighlightType: props.containerHighlightType,
}), }),
); );

View File

@ -81,6 +81,7 @@ watchEffect(() => {
isContainer: stageOptions.isContainer, isContainer: stageOptions.isContainer,
containerHighlightClassName: stageOptions.containerHighlightClassName, containerHighlightClassName: stageOptions.containerHighlightClassName,
containerHighlightDuration: stageOptions.containerHighlightDuration, containerHighlightDuration: stageOptions.containerHighlightDuration,
containerHighlightType: stageOptions.containerHighlightType,
canSelect: (el, event, stop) => { canSelect: (el, event, stop) => {
const elCanSelect = stageOptions.canSelect(el); const elCanSelect = stageOptions.canSelect(el);
// ui-select // ui-select

View File

@ -21,7 +21,7 @@ import type { Component } from 'vue';
import type { FormConfig } from '@tmagic/form'; import type { FormConfig } from '@tmagic/form';
import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema'; import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
import type StageCore from '@tmagic/stage'; import type StageCore from '@tmagic/stage';
import type { MoveableOptions } from '@tmagic/stage'; import type { ContainerHighlightType, MoveableOptions } from '@tmagic/stage';
import type { ComponentListService } from './services/componentList'; import type { ComponentListService } from './services/componentList';
import type { EditorService } from './services/editor'; import type { EditorService } from './services/editor';
@ -53,6 +53,7 @@ export interface StageOptions {
autoScrollIntoView: boolean; autoScrollIntoView: boolean;
containerHighlightClassName: string; containerHighlightClassName: string;
containerHighlightDuration: number; containerHighlightDuration: number;
containerHighlightType: ContainerHighlightType;
render: () => HTMLDivElement; render: () => HTMLDivElement;
moveableOptions: MoveableOptions | ((core?: StageCore) => MoveableOptions); moveableOptions: MoveableOptions | ((core?: StageCore) => MoveableOptions);
canSelect: (el: HTMLElement) => boolean | Promise<boolean>; canSelect: (el: HTMLElement) => boolean | Promise<boolean>;

View File

@ -21,7 +21,7 @@ import { EventEmitter } from 'events';
import type { Id } from '@tmagic/schema'; import type { Id } from '@tmagic/schema';
import { addClassName } from '@tmagic/utils'; import { addClassName } from '@tmagic/utils';
import { DEFAULT_ZOOM, GHOST_EL_ID_PREFIX, PAGE_CLASS } from './const'; import { CONTAINER_HIGHLIGHT_CLASS, DEFAULT_ZOOM, GHOST_EL_ID_PREFIX, PAGE_CLASS } from './const';
import StageDragResize from './StageDragResize'; import StageDragResize from './StageDragResize';
import StageHighlight from './StageHighlight'; import StageHighlight from './StageHighlight';
import StageMask from './StageMask'; import StageMask from './StageMask';
@ -29,6 +29,7 @@ import StageMultiDragResize from './StageMultiDragResize';
import StageRender from './StageRender'; import StageRender from './StageRender';
import { import {
CanSelect, CanSelect,
ContainerHighlightType,
GuidesEventData, GuidesEventData,
IsContainer, IsContainer,
RemoveData, RemoveData,
@ -57,6 +58,7 @@ export default class StageCore extends EventEmitter {
public zoom = DEFAULT_ZOOM; public zoom = DEFAULT_ZOOM;
public containerHighlightClassName: string; public containerHighlightClassName: string;
public containerHighlightDuration: number; public containerHighlightDuration: number;
public containerHighlightType?: ContainerHighlightType;
public isContainer: IsContainer; public isContainer: IsContainer;
private canSelect: CanSelect; private canSelect: CanSelect;
@ -69,8 +71,9 @@ export default class StageCore extends EventEmitter {
this.setZoom(config.zoom); this.setZoom(config.zoom);
this.canSelect = config.canSelect || ((el: HTMLElement) => !!el.id); this.canSelect = config.canSelect || ((el: HTMLElement) => !!el.id);
this.isContainer = config.isContainer; this.isContainer = config.isContainer;
this.containerHighlightClassName = config.containerHighlightClassName; this.containerHighlightClassName = config.containerHighlightClassName || CONTAINER_HIGHLIGHT_CLASS;
this.containerHighlightDuration = config.containerHighlightDuration; this.containerHighlightDuration = config.containerHighlightDuration || 800;
this.containerHighlightType = config.containerHighlightType;
this.renderer = new StageRender({ core: this }); this.renderer = new StageRender({ core: this });
this.mask = new StageMask({ core: this }); this.mask = new StageMask({ core: this });

View File

@ -19,6 +19,7 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import KeyController from 'keycon';
import type { MoveableOptions } from 'moveable'; import type { MoveableOptions } from 'moveable';
import Moveable from 'moveable'; import Moveable from 'moveable';
import MoveableHelper from 'moveable-helper'; import MoveableHelper from 'moveable-helper';
@ -29,7 +30,7 @@ import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, Mode, ZIndex } from
import StageCore from './StageCore'; import StageCore from './StageCore';
import StageMask from './StageMask'; import StageMask from './StageMask';
import type { StageDragResizeConfig } from './types'; import type { StageDragResizeConfig } from './types';
import { StageDragStatus } from './types'; import { ContainerHighlightType, StageDragStatus } from './types';
import { calcValueByFontsize, down, getAbsolutePosition, getMode, getOffset, getTargetElStyle, up } from './util'; import { calcValueByFontsize, down, getAbsolutePosition, getMode, getOffset, getTargetElStyle, up } from './util';
/** /**
@ -61,6 +62,7 @@ export default class StageDragResize extends EventEmitter {
/** 流式布局下,目标节点的镜像节点 */ /** 流式布局下,目标节点的镜像节点 */
private ghostEl: HTMLElement | undefined; private ghostEl: HTMLElement | undefined;
private moveableHelper?: MoveableHelper; private moveableHelper?: MoveableHelper;
private isContainerHighlight: Boolean = false;
constructor(config: StageDragResizeConfig) { constructor(config: StageDragResizeConfig) {
super(); super();
@ -68,6 +70,20 @@ export default class StageDragResize extends EventEmitter {
this.core = config.core; this.core = config.core;
this.container = config.container; this.container = config.container;
this.mask = config.mask; this.mask = config.mask;
KeyController.global.keydown('alt', (e) => {
e.inputEvent.preventDefault();
this.isContainerHighlight = true;
});
KeyController.global.keyup('alt', (e) => {
e.inputEvent.preventDefault();
const doc = this.core.renderer.contentWindow?.document;
if (doc && this.canContainerHighlight()) {
removeClassNameByClassName(doc, this.core.containerHighlightClassName);
}
this.isContainerHighlight = false;
});
} }
/** /**
@ -302,7 +318,9 @@ export default class StageDragResize extends EventEmitter {
timeout = undefined; timeout = undefined;
} }
timeout = this.core.getAddContainerHighlightClassNameTimeout(e.inputEvent, [this.target]); if (this.canContainerHighlight()) {
timeout = this.core.getAddContainerHighlightClassNameTimeout(e.inputEvent, [this.target]);
}
this.dragStatus = StageDragStatus.ING; this.dragStatus = StageDragStatus.ING;
@ -325,7 +343,7 @@ export default class StageDragResize extends EventEmitter {
let parentEl: HTMLElement | null = null; let parentEl: HTMLElement | null = null;
if (doc) { if (doc && this.canContainerHighlight()) {
parentEl = removeClassNameByClassName(doc, this.core.containerHighlightClassName); parentEl = removeClassNameByClassName(doc, this.core.containerHighlightClassName);
} }
@ -561,4 +579,11 @@ export default class StageDragResize extends EventEmitter {
...moveableOptions, ...moveableOptions,
}; };
} }
private canContainerHighlight() {
return (
this.core.containerHighlightType === ContainerHighlightType.DEFAULT ||
(this.core.containerHighlightType === ContainerHighlightType.ALT && this.isContainerHighlight)
);
}
} }

View File

@ -29,6 +29,11 @@ import StageMask from './StageMask';
export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>; export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
export type IsContainer = (el: HTMLElement) => boolean | Promise<boolean>; export type IsContainer = (el: HTMLElement) => boolean | Promise<boolean>;
export enum ContainerHighlightType {
DEFAULT = 'default',
ALT = 'alt',
}
export type StageCoreConfig = { export type StageCoreConfig = {
/** 需要对齐的dom节点的CSS选择器字符串 */ /** 需要对齐的dom节点的CSS选择器字符串 */
snapElementQuerySelector?: string; snapElementQuerySelector?: string;
@ -36,8 +41,9 @@ export type StageCoreConfig = {
zoom?: number; zoom?: number;
canSelect?: CanSelect; canSelect?: CanSelect;
isContainer: IsContainer; isContainer: IsContainer;
containerHighlightClassName: string; containerHighlightClassName?: string;
containerHighlightDuration: number; containerHighlightDuration?: number;
containerHighlightType?: ContainerHighlightType;
moveableOptions?: ((core?: StageCore) => MoveableOptions) | MoveableOptions; moveableOptions?: ((core?: StageCore) => MoveableOptions) | MoveableOptions;
multiMoveableOptions?: ((core?: StageCore) => MoveableOptions) | MoveableOptions; multiMoveableOptions?: ((core?: StageCore) => MoveableOptions) | MoveableOptions;
/** runtime 的HTML地址可以是一个HTTP地址如果和编辑器不同域需要设置跨域也可以是一个相对或绝对路径 */ /** runtime 的HTML地址可以是一个HTTP地址如果和编辑器不同域需要设置跨域也可以是一个相对或绝对路径 */