- {!dragging &&
}
+
+ {(!dragging && !hideComponentAction) ? : null}
);
}
diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts
index eb855e449..57f856932 100644
--- a/packages/designer/src/builtin-simulator/host.ts
+++ b/packages/designer/src/builtin-simulator/host.ts
@@ -39,6 +39,7 @@ import {
isDragAnyObject,
isDragNodeObject,
isLocationData,
+ Logger,
} from '@alilc/lowcode-utils';
import {
isShaken,
@@ -72,6 +73,8 @@ import { IScroller } from '../designer/scroller';
import { isElementNode, isDOMNodeVisible } from '../utils/misc';
import { debounce } from 'lodash';
+const logger = new Logger({ level: 'warn', bizName: 'designer' });
+
export type LibraryItem = IPublicTypePackage & {
package: string;
library: string;
@@ -122,21 +125,6 @@ const defaultSimulatorUrl = (() => {
return urls;
})();
-const defaultRaxSimulatorUrl = (() => {
- const publicPath = getPublicPath();
- let urls;
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [_, prefix = '', dev] = /^(.+?)(\/js)?\/?$/.exec(publicPath) || [];
- if (dev) {
- urls = [`${prefix}/css/rax-simulator-renderer.css`, `${prefix}/js/rax-simulator-renderer.js`];
- } else if (process.env.NODE_ENV === 'production') {
- urls = [`${prefix}/rax-simulator-renderer.css`, `${prefix}/rax-simulator-renderer.js`];
- } else {
- urls = [`${prefix}/rax-simulator-renderer.css`, `${prefix}/rax-simulator-renderer.js`];
- }
- return urls;
-})();
-
const defaultEnvironment = [
// https://g.alicdn.com/mylib/??react/16.11.0/umd/react.production.min.js,react-dom/16.8.6/umd/react-dom.production.min.js,prop-types/15.7.2/prop-types.min.js
assetItem(
@@ -151,17 +139,6 @@ const defaultEnvironment = [
),
];
-const defaultRaxEnvironment = [
- assetItem(
- AssetType.JSText,
- 'window.Rax=parent.Rax;window.React=parent.React;window.ReactDOM=parent.ReactDOM;window.VisualEngineUtils=parent.VisualEngineUtils;window.VisualEngine=parent.VisualEngine',
- ),
- assetItem(
- AssetType.JSText,
- 'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
- ),
-];
-
export class BuiltinSimulatorHost implements ISimulatorHost
{
readonly isSimulator = true;
@@ -467,11 +444,15 @@ export class BuiltinSimulatorHost implements ISimulatorHost {
const targetElement = e.target as HTMLElement;
const nodeInst = this.getNodeInstanceFromElement(targetElement);
+ const editor = this.designer?.editor;
if (!nodeInst) {
+ editor?.eventBus.emit('designer.builtinSimulator.contextmenu', {
+ originalEvent: e,
+ });
return;
}
const node = nodeInst.node || this.project.currentDocument?.focusNode;
if (!node) {
+ editor?.eventBus.emit('designer.builtinSimulator.contextmenu', {
+ originalEvent: e,
+ });
return;
}
// dirty code should refector
- const editor = this.designer?.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts
index 00c495621..1ee1154f1 100644
--- a/packages/designer/src/component-meta.ts
+++ b/packages/designer/src/component-meta.ts
@@ -48,13 +48,17 @@ export function buildFilter(rule?: string | string[] | RegExp | IPublicTypeNesti
return rule;
}
if (isRegExp(rule)) {
- return (testNode: Node | IPublicTypeNodeSchema) => rule.test(testNode.componentName);
+ return (testNode: Node | IPublicTypeNodeSchema) => {
+ return rule.test(testNode.componentName);
+ };
}
const list = ensureAList(rule);
if (!list) {
return null;
}
- return (testNode: Node | IPublicTypeNodeSchema) => list.includes(testNode.componentName);
+ return (testNode: Node | IPublicTypeNodeSchema) => {
+ return list.includes(testNode.componentName);
+ };
}
export interface IComponentMeta extends IPublicModelComponentMeta {
diff --git a/packages/designer/src/context-menu-actions.scss b/packages/designer/src/context-menu-actions.scss
new file mode 100644
index 000000000..863c92944
--- /dev/null
+++ b/packages/designer/src/context-menu-actions.scss
@@ -0,0 +1,10 @@
+.engine-context-menu {
+ &.next-menu.next-ver .next-menu-item {
+ padding-right: 30px;
+
+ .next-menu-item-inner {
+ height: var(--context-menu-item-height, 30px);
+ line-height: var(--context-menu-item-height, 30px);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/designer/src/context-menu-actions.ts b/packages/designer/src/context-menu-actions.ts
new file mode 100644
index 000000000..c88e03ac6
--- /dev/null
+++ b/packages/designer/src/context-menu-actions.ts
@@ -0,0 +1,233 @@
+import { IPublicTypeContextMenuAction, IPublicEnumContextMenuType, IPublicTypeContextMenuItem, IPublicApiMaterial, IPublicModelPluginContext } from '@alilc/lowcode-types';
+import { IDesigner, INode } from './designer';
+import { createContextMenu, parseContextMenuAsReactNode, parseContextMenuProperties, uniqueId } from '@alilc/lowcode-utils';
+import { Menu } from '@alifd/next';
+import { engineConfig } from '@alilc/lowcode-editor-core';
+import './context-menu-actions.scss';
+
+export interface IContextMenuActions {
+ actions: IPublicTypeContextMenuAction[];
+
+ adjustMenuLayoutFn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[];
+
+ addMenuAction: IPublicApiMaterial['addContextMenuOption'];
+
+ removeMenuAction: IPublicApiMaterial['removeContextMenuOption'];
+
+ adjustMenuLayout: IPublicApiMaterial['adjustContextMenuLayout'];
+}
+
+let adjustMenuLayoutFn: Function = (actions: IPublicTypeContextMenuAction[]) => actions;
+
+export class GlobalContextMenuActions {
+ enableContextMenu: boolean;
+
+ dispose: Function[];
+
+ contextMenuActionsMap: Map = new Map();
+
+ constructor() {
+ this.dispose = [];
+
+ engineConfig.onGot('enableContextMenu', (enable) => {
+ if (this.enableContextMenu === enable) {
+ return;
+ }
+ this.enableContextMenu = enable;
+ this.dispose.forEach(d => d());
+ if (enable) {
+ this.initEvent();
+ }
+ });
+ }
+
+ handleContextMenu = (
+ event: MouseEvent,
+ ) => {
+ event.stopPropagation();
+ event.preventDefault();
+
+ const actions: IPublicTypeContextMenuAction[] = [];
+ let contextMenu: ContextMenuActions = this.contextMenuActionsMap.values().next().value;
+ this.contextMenuActionsMap.forEach((contextMenu) => {
+ actions.push(...contextMenu.actions);
+ });
+
+ let destroyFn: Function | undefined;
+
+ const destroy = () => {
+ destroyFn?.();
+ };
+ const pluginContext: IPublicModelPluginContext = contextMenu.designer.editor.get('pluginContext') as IPublicModelPluginContext;
+
+ const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
+ nodes: [],
+ destroy,
+ event,
+ pluginContext,
+ });
+
+ if (!menus.length) {
+ return;
+ }
+
+ const layoutMenu = adjustMenuLayoutFn(menus);
+
+ const menuNode = parseContextMenuAsReactNode(layoutMenu, {
+ destroy,
+ nodes: [],
+ pluginContext,
+ });
+
+ const target = event.target;
+
+ const { top, left } = target?.getBoundingClientRect();
+
+ const menuInstance = Menu.create({
+ target: event.target,
+ offset: [event.clientX - left, event.clientY - top],
+ children: menuNode,
+ className: 'engine-context-menu',
+ });
+
+ destroyFn = (menuInstance as any).destroy;
+ };
+
+ initEvent() {
+ this.dispose.push(
+ (() => {
+ const handleContextMenu = (e: MouseEvent) => {
+ this.handleContextMenu(e);
+ };
+
+ document.addEventListener('contextmenu', handleContextMenu);
+
+ return () => {
+ document.removeEventListener('contextmenu', handleContextMenu);
+ };
+ })(),
+ );
+ }
+
+ registerContextMenuActions(contextMenu: ContextMenuActions) {
+ this.contextMenuActionsMap.set(contextMenu.id, contextMenu);
+ }
+}
+
+const globalContextMenuActions = new GlobalContextMenuActions();
+
+export class ContextMenuActions implements IContextMenuActions {
+ actions: IPublicTypeContextMenuAction[] = [];
+
+ designer: IDesigner;
+
+ dispose: Function[];
+
+ enableContextMenu: boolean;
+
+ id: string = uniqueId('contextMenu');;
+
+ constructor(designer: IDesigner) {
+ this.designer = designer;
+ this.dispose = [];
+
+ engineConfig.onGot('enableContextMenu', (enable) => {
+ if (this.enableContextMenu === enable) {
+ return;
+ }
+ this.enableContextMenu = enable;
+ this.dispose.forEach(d => d());
+ if (enable) {
+ this.initEvent();
+ }
+ });
+
+ globalContextMenuActions.registerContextMenuActions(this);
+ }
+
+ handleContextMenu = (
+ nodes: INode[],
+ event: MouseEvent,
+ ) => {
+ const designer = this.designer;
+ event.stopPropagation();
+ event.preventDefault();
+
+ const actions = designer.contextMenuActions.actions;
+
+ const { bounds } = designer.project.simulator?.viewport || { bounds: { left: 0, top: 0 } };
+ const { left: simulatorLeft, top: simulatorTop } = bounds;
+
+ let destroyFn: Function | undefined;
+
+ const destroy = () => {
+ destroyFn?.();
+ };
+
+ const pluginContext: IPublicModelPluginContext = this.designer.editor.get('pluginContext') as IPublicModelPluginContext;
+
+ const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
+ nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
+ destroy,
+ event,
+ pluginContext,
+ });
+
+ if (!menus.length) {
+ return;
+ }
+
+ const layoutMenu = adjustMenuLayoutFn(menus);
+
+ const menuNode = parseContextMenuAsReactNode(layoutMenu, {
+ destroy,
+ nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
+ pluginContext,
+ });
+
+ destroyFn = createContextMenu(menuNode, {
+ event,
+ offset: [simulatorLeft, simulatorTop],
+ });
+ };
+
+ initEvent() {
+ const designer = this.designer;
+ this.dispose.push(
+ designer.editor.eventBus.on('designer.builtinSimulator.contextmenu', ({
+ node,
+ originalEvent,
+ }: {
+ node: INode;
+ originalEvent: MouseEvent;
+ }) => {
+ originalEvent.stopPropagation();
+ originalEvent.preventDefault();
+ // 如果右键的节点不在 当前选中的节点中,选中该节点
+ if (!designer.currentSelection.has(node.id)) {
+ designer.currentSelection.select(node.id);
+ }
+ const nodes = designer.currentSelection.getNodes();
+ this.handleContextMenu(nodes, originalEvent);
+ }),
+ );
+ }
+
+ addMenuAction(action: IPublicTypeContextMenuAction) {
+ this.actions.push({
+ type: IPublicEnumContextMenuType.MENU_ITEM,
+ ...action,
+ });
+ }
+
+ removeMenuAction(name: string) {
+ const i = this.actions.findIndex((action) => action.name === name);
+ if (i > -1) {
+ this.actions.splice(i, 1);
+ }
+ }
+
+ adjustMenuLayout(fn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[]) {
+ adjustMenuLayoutFn = fn;
+ }
+}
\ No newline at end of file
diff --git a/packages/designer/src/designer/clipboard.ts b/packages/designer/src/designer/clipboard.ts
index 941f91442..34ce2b5b5 100644
--- a/packages/designer/src/designer/clipboard.ts
+++ b/packages/designer/src/designer/clipboard.ts
@@ -36,6 +36,10 @@ class Clipboard implements IClipboard {
private waitFn?: (data: any, e: ClipboardEvent) => void;
+ constructor() {
+ this.injectCopyPaster(document);
+ }
+
isCopyPasteEvent(e: Event) {
this.isCopyPaster(e.target);
}
@@ -69,7 +73,13 @@ class Clipboard implements IClipboard {
}
const copyPaster = document.createElement<'textarea'>('textarea');
copyPaster.style.cssText = 'position: absolute;left: -9999px;top:-100px';
- document.body.appendChild(copyPaster);
+ if (document.body) {
+ document.body.appendChild(copyPaster);
+ } else {
+ document.addEventListener('DOMContentLoaded', () => {
+ document.body.appendChild(copyPaster);
+ });
+ }
const dispose = this.initCopyPaster(copyPaster);
return () => {
dispose();
diff --git a/packages/designer/src/designer/designer-view.tsx b/packages/designer/src/designer/designer-view.tsx
index 0db2a3fa3..aaf0c9583 100644
--- a/packages/designer/src/designer/designer-view.tsx
+++ b/packages/designer/src/designer/designer-view.tsx
@@ -4,7 +4,6 @@ import BuiltinDragGhostComponent from './drag-ghost';
import { Designer, DesignerProps } from './designer';
import { ProjectView } from '../project';
import './designer.less';
-import { clipboard } from './clipboard';
type IProps = DesignerProps & {
designer?: Designer;
@@ -44,7 +43,6 @@ export class DesignerView extends Component {
if (onMount) {
onMount(this.designer);
}
- clipboard.injectCopyPaster(document);
this.designer.postEvent('mount', this.designer);
}
diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts
index d7e17e84c..1dd4bc04e 100644
--- a/packages/designer/src/designer/designer.ts
+++ b/packages/designer/src/designer/designer.ts
@@ -20,7 +20,7 @@ import {
} from '@alilc/lowcode-types';
import { mergeAssets, IPublicTypeAssetsJson, isNodeSchema, isDragNodeObject, isDragNodeDataObject, isLocationChildrenDetail, Logger } from '@alilc/lowcode-utils';
import { IProject, Project } from '../project';
-import { Node, DocumentModel, insertChildren, INode } from '../document';
+import { Node, DocumentModel, insertChildren, INode, ISelection } from '../document';
import { ComponentMeta, IComponentMeta } from '../component-meta';
import { INodeSelector, Component } from '../simulator';
import { Scroller } from './scroller';
@@ -32,6 +32,7 @@ import { OffsetObserver, createOffsetObserver } from './offset-observer';
import { ISettingTopEntry, SettingTopEntry } from './setting';
import { BemToolsManager } from '../builtin-simulator/bem-tools/manager';
import { ComponentActions } from '../component-actions';
+import { ContextMenuActions, IContextMenuActions } from '../context-menu-actions';
const logger = new Logger({ level: 'warn', bizName: 'designer' });
@@ -72,12 +73,16 @@ export interface IDesigner {
get componentActions(): ComponentActions;
+ get contextMenuActions(): ContextMenuActions;
+
get editor(): IPublicModelEditor;
get detecting(): Detecting;
get simulatorComponent(): ComponentType | undefined;
+ get currentSelection(): ISelection;
+
createScroller(scrollable: IPublicTypeScrollable): IPublicModelScroller;
refreshComponentMetasMap(): void;
@@ -122,6 +127,8 @@ export class Designer implements IDesigner {
readonly componentActions = new ComponentActions();
+ readonly contextMenuActions: IContextMenuActions;
+
readonly activeTracker = new ActiveTracker();
readonly detecting = new Detecting();
@@ -198,6 +205,8 @@ export class Designer implements IDesigner {
this.postEvent('dragstart', e);
});
+ this.contextMenuActions = new ContextMenuActions(this);
+
this.dragon.onDrag((e) => {
if (this.props?.onDrag) {
this.props.onDrag(e);
diff --git a/packages/designer/src/designer/setting/setting-top-entry.ts b/packages/designer/src/designer/setting/setting-top-entry.ts
index f89ff8a62..85be74b7f 100644
--- a/packages/designer/src/designer/setting/setting-top-entry.ts
+++ b/packages/designer/src/designer/setting/setting-top-entry.ts
@@ -1,4 +1,4 @@
-import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry } from '@alilc/lowcode-types';
+import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry, IPublicApiSetters } from '@alilc/lowcode-types';
import { isCustomView } from '@alilc/lowcode-utils';
import { computed, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { ISettingEntry } from './setting-entry-type';
@@ -6,7 +6,6 @@ import { ISettingField, SettingField } from './setting-field';
import { INode } from '../../document';
import type { IComponentMeta } from '../../component-meta';
import { IDesigner } from '../designer';
-import { Setters } from '@alilc/lowcode-shell';
function generateSessionId(nodes: INode[]) {
return nodes
@@ -19,18 +18,18 @@ export interface ISettingTopEntry extends ISettingEntry, IPublicModelSettingTopE
INode,
ISettingField
> {
- purge(): void;
-
- items: Array;
-
readonly top: ISettingTopEntry;
readonly parent: ISettingTopEntry;
readonly path: never[];
+ items: Array;
+
componentMeta: IComponentMeta | null;
+ purge(): void;
+
getExtraPropValue(propName: string): void;
setExtraPropValue(propName: string, value: any): void;
@@ -92,7 +91,7 @@ export class SettingTopEntry implements ISettingTopEntry {
readonly designer: IDesigner | undefined;
- readonly setters: Setters;
+ readonly setters: IPublicApiSetters;
disposeFunctions: any[] = [];
@@ -103,7 +102,7 @@ export class SettingTopEntry implements ISettingTopEntry {
this.id = generateSessionId(nodes);
this.first = nodes[0];
this.designer = this.first.document?.designer;
- this.setters = editor.get('setters') as Setters;
+ this.setters = editor.get('setters') as IPublicApiSetters;
// setups
this.setupComponentMeta();
diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts
index 4071fb913..c8363d058 100644
--- a/packages/designer/src/document/node/node.ts
+++ b/packages/designer/src/document/node/node.ts
@@ -392,7 +392,7 @@ export class Node
this.isInited = true;
this.emitter = createModuleEventBus('Node');
- const editor = this.document.designer.editor;
+ const { editor } = this.document.designer;
this.onVisibleChange((visible: boolean) => {
editor?.eventBus.emit(EDITOR_EVENT.NODE_VISIBLE_CHANGE, this, visible);
});
@@ -1219,11 +1219,18 @@ export class Node
/**
* 获取磁贴相关信息
*/
- getRGL() {
+ getRGL(): {
+ isContainerNode: boolean;
+ isEmptyNode: boolean;
+ isRGLContainerNode: boolean;
+ isRGLNode: boolean;
+ isRGL: boolean;
+ rglNode: Node | null;
+ } {
const isContainerNode = this.isContainer();
const isEmptyNode = this.isEmpty();
const isRGLContainerNode = this.isRGLContainer;
- const isRGLNode = this.getParent()?.isRGLContainer;
+ const isRGLNode = (this.getParent()?.isRGLContainer) as boolean;
const isRGL = isRGLContainerNode || (isRGLNode && (!isContainerNode || !isEmptyNode));
let rglNode = isRGLContainerNode ? this : isRGL ? this?.getParent() : null;
return { isContainerNode, isEmptyNode, isRGLContainerNode, isRGLNode, isRGL, rglNode };
diff --git a/packages/designer/src/index.ts b/packages/designer/src/index.ts
index 489d81482..11e6453b8 100644
--- a/packages/designer/src/index.ts
+++ b/packages/designer/src/index.ts
@@ -6,3 +6,4 @@ export * from './project';
export * from './builtin-simulator';
export * from './plugin';
export * from './types';
+export * from './context-menu-actions';
diff --git a/packages/designer/src/plugin/plugin-context.ts b/packages/designer/src/plugin/plugin-context.ts
index 94e296fd1..7f26a2b4f 100644
--- a/packages/designer/src/plugin/plugin-context.ts
+++ b/packages/designer/src/plugin/plugin-context.ts
@@ -19,6 +19,7 @@ import {
IPublicApiWorkspace,
IPublicEnumPluginRegisterLevel,
IPublicModelWindow,
+ IPublicApiCommonUI,
} from '@alilc/lowcode-types';
import {
IPluginContextOptions,
@@ -45,6 +46,8 @@ export default class PluginContext implements
workspace: IPublicApiWorkspace;
registerLevel: IPublicEnumPluginRegisterLevel;
editorWindow: IPublicModelWindow;
+ commonUI: IPublicApiCommonUI;
+ isPluginRegisteredInWorkspace: false;
constructor(
options: IPluginContextOptions,
diff --git a/packages/designer/src/plugin/plugin-types.ts b/packages/designer/src/plugin/plugin-types.ts
index 6091170f0..d648067a3 100644
--- a/packages/designer/src/plugin/plugin-types.ts
+++ b/packages/designer/src/plugin/plugin-types.ts
@@ -18,6 +18,7 @@ import {
IPublicTypePluginRegisterOptions,
IPublicModelWindow,
IPublicEnumPluginRegisterLevel,
+ IPublicApiCommonUI,
} from '@alilc/lowcode-types';
import PluginContext from './plugin-context';
@@ -61,6 +62,7 @@ export interface ILowCodePluginContextPrivate {
set editorWindow(window: IPublicModelWindow);
set registerLevel(level: IPublicEnumPluginRegisterLevel);
set isPluginRegisteredInWorkspace(flag: boolean);
+ set commonUI(commonUI: IPublicApiCommonUI);
}
export interface ILowCodePluginContextApiAssembler {
assembleApis(
diff --git a/packages/editor-core/package.json b/packages/editor-core/package.json
index 087558450..888642adf 100644
--- a/packages/editor-core/package.json
+++ b/packages/editor-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-editor-core",
- "version": "1.2.5",
+ "version": "1.3.0",
"description": "Core Api for Ali lowCode engine",
"license": "MIT",
"main": "lib/index.js",
@@ -14,8 +14,8 @@
},
"dependencies": {
"@alifd/next": "^1.19.16",
- "@alilc/lowcode-types": "1.2.5",
- "@alilc/lowcode-utils": "1.2.5",
+ "@alilc/lowcode-types": "1.3.0",
+ "@alilc/lowcode-utils": "1.3.0",
"classnames": "^2.2.6",
"debug": "^4.1.1",
"intl-messageformat": "^9.3.1",
diff --git a/packages/editor-core/src/config.ts b/packages/editor-core/src/config.ts
index 85e609780..c4ff407b9 100644
--- a/packages/editor-core/src/config.ts
+++ b/packages/editor-core/src/config.ts
@@ -44,7 +44,7 @@ const VALID_ENGINE_OPTIONS = {
},
renderEnv: {
type: 'string',
- enum: ['react', 'rax', 'any string value'],
+ enum: ['react', 'any string value'],
default: 'react',
description: '渲染器类型',
},
@@ -159,6 +159,16 @@ const VALID_ENGINE_OPTIONS = {
type: 'function',
description: '应用级设计模式下,窗口为空时展示的占位组件',
},
+ enableContextMenu: {
+ type: 'boolean',
+ description: '是否开启右键菜单',
+ default: false,
+ },
+ hideComponentAction: {
+ type: 'boolean',
+ description: '是否隐藏设计器辅助层',
+ default: false,
+ },
};
const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValue: boolean): boolean => {
diff --git a/packages/editor-core/src/intl/index.ts b/packages/editor-core/src/intl/index.ts
index 6d9d840c3..99e99a4fb 100644
--- a/packages/editor-core/src/intl/index.ts
+++ b/packages/editor-core/src/intl/index.ts
@@ -3,6 +3,7 @@ import { IntlMessageFormat } from 'intl-messageformat';
import { globalLocale } from './global-locale';
import { isI18nData } from '@alilc/lowcode-utils';
import { observer } from '../utils';
+import { IPublicTypeI18nData } from '@alilc/lowcode-types';
function generateTryLocales(locale: string) {
const tries = [locale, locale.replace('-', '_')];
@@ -26,18 +27,9 @@ function injectVars(msg: string, params: any, locale: string): string {
}
const formater = new IntlMessageFormat(msg, locale);
return formater.format(params as any) as string;
- /*
-
- return template.replace(/({\w+})/g, (_, $1) => {
- const key = (/\d+/.exec($1) || [])[0] as any;
- if (key && params[key] != null) {
- return params[key];
- }
- return $1;
- }); */
}
-export function intl(data: any, params?: object): ReactNode {
+export function intl(data: IPublicTypeI18nData | string, params?: object): ReactNode {
if (!isI18nData(data)) {
return data;
}
diff --git a/packages/editor-core/src/widgets/tip/help-tips.tsx b/packages/editor-core/src/widgets/tip/help-tips.tsx
new file mode 100644
index 000000000..ab5f65050
--- /dev/null
+++ b/packages/editor-core/src/widgets/tip/help-tips.tsx
@@ -0,0 +1,40 @@
+import { IPublicTypeHelpTipConfig, IPublicTypeTipConfig } from '@alilc/lowcode-types';
+import { Tip } from './tip';
+import { Icon } from '@alifd/next';
+import { IconProps } from '@alifd/next/types/icon';
+
+export function HelpTip({
+ help,
+ direction = 'top',
+ size = 'small',
+}: {
+ help: IPublicTypeHelpTipConfig;
+ direction?: IPublicTypeTipConfig['direction'];
+ size?: IconProps['size'];
+}) {
+ if (typeof help === 'string') {
+ return (
+
+
+ {help}
+
+ );
+ }
+
+ if (typeof help === 'object' && help.url) {
+ return (
+
+ );
+ }
+ return (
+
+
+ {help.content}
+
+ );
+}
\ No newline at end of file
diff --git a/packages/editor-core/src/widgets/tip/index.ts b/packages/editor-core/src/widgets/tip/index.ts
index dba4412c7..d2b376800 100644
--- a/packages/editor-core/src/widgets/tip/index.ts
+++ b/packages/editor-core/src/widgets/tip/index.ts
@@ -2,3 +2,4 @@ import './style.less';
export * from './tip';
export * from './tip-container';
+export * from './help-tips';
diff --git a/packages/editor-core/src/widgets/title/index.tsx b/packages/editor-core/src/widgets/title/index.tsx
index 88a15ab29..7df2676f9 100644
--- a/packages/editor-core/src/widgets/title/index.tsx
+++ b/packages/editor-core/src/widgets/title/index.tsx
@@ -1,7 +1,7 @@
import { Component, isValidElement, ReactNode } from 'react';
import classNames from 'classnames';
import { createIcon, isI18nData, isTitleConfig } from '@alilc/lowcode-utils';
-import { IPublicTypeTitleContent, IPublicTypeI18nData, IPublicTypeTitleConfig } from '@alilc/lowcode-types';
+import { IPublicTypeI18nData, IPublicTypeTitleConfig, IPublicTypeTitleProps } from '@alilc/lowcode-types';
import { intl } from '../../intl';
import { Tip } from '../tip';
import './title.less';
@@ -36,13 +36,7 @@ import './title.less';
return fragments;
}
-export class Title extends Component<{
- title: IPublicTypeTitleContent;
- className?: string;
- onClick?: () => void;
- match?: boolean;
- keywords?: string;
-}> {
+export class Title extends Component {
constructor(props: any) {
super(props);
this.handleClick = this.handleClick.bind(this);
diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json
index 648ed5be1..58c5f2e18 100644
--- a/packages/editor-skeleton/package.json
+++ b/packages/editor-skeleton/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-editor-skeleton",
- "version": "1.2.5",
+ "version": "1.3.0",
"description": "alibaba lowcode editor skeleton",
"main": "lib/index.js",
"module": "es/index.js",
@@ -19,10 +19,10 @@
],
"dependencies": {
"@alifd/next": "^1.20.12",
- "@alilc/lowcode-designer": "1.2.5",
- "@alilc/lowcode-editor-core": "1.2.5",
- "@alilc/lowcode-types": "1.2.5",
- "@alilc/lowcode-utils": "1.2.5",
+ "@alilc/lowcode-designer": "1.3.0",
+ "@alilc/lowcode-editor-core": "1.3.0",
+ "@alilc/lowcode-types": "1.3.0",
+ "@alilc/lowcode-utils": "1.3.0",
"classnames": "^2.2.6",
"react": "^16.8.1",
"react-dom": "^16.8.1"
diff --git a/packages/editor-skeleton/src/components/widget-views/index.tsx b/packages/editor-skeleton/src/components/widget-views/index.tsx
index 2b2f6931c..7cdff4c01 100644
--- a/packages/editor-skeleton/src/components/widget-views/index.tsx
+++ b/packages/editor-skeleton/src/components/widget-views/index.tsx
@@ -1,7 +1,6 @@
import { Component, ReactElement } from 'react';
-import { Icon } from '@alifd/next';
import classNames from 'classnames';
-import { Title, observer, Tip } from '@alilc/lowcode-editor-core';
+import { Title, observer, HelpTip } from '@alilc/lowcode-editor-core';
import { DockProps } from '../../types';
import { PanelDock } from '../../widget/panel-dock';
import { composeTitle } from '../../widget/utils';
@@ -26,25 +25,6 @@ export function DockView({ title, icon, description, size, className, onClick }:
);
}
-function HelpTip({ tip }: any) {
- if (tip && tip.url) {
- return (
-
- );
- }
- return (
-
-
- {tip.content}
-
- );
-}
-
@observer
export class PanelDockView extends Component {
private lastActived = false;
@@ -328,7 +308,7 @@ class PanelTitle extends Component<{ panel: Panel; className?: string }> {
data-name={panel.name}
>
- {panel.help ? : null}
+ {panel.help ? : null}
);
}
diff --git a/packages/engine/README-zh_CN.md b/packages/engine/README-zh_CN.md
index c99e98cf6..5442aa58b 100644
--- a/packages/engine/README-zh_CN.md
+++ b/packages/engine/README-zh_CN.md
@@ -126,7 +126,7 @@ https://cdn.jsdelivr.net/npm/@alilc/lowcode-react-simulator-renderer@1.0.18/dist
```
#### 方式 5:使用自有 cdn
-将源码中 packages/engine/dist 和 packages/(react|rax)-simulator-renderer/dist 下的文件传至你的 cdn 提供商
+将源码中 packages/engine/dist 和 packages/react-simulator-renderer/dist 下的文件传至你的 cdn 提供商
## 🔗 相关链接
diff --git a/packages/engine/README.md b/packages/engine/README.md
index 52bce8616..ae4e7fd43 100644
--- a/packages/engine/README.md
+++ b/packages/engine/README.md
@@ -126,7 +126,7 @@ https://cdn.jsdelivr.net/npm/@alilc/lowcode-react-simulator-renderer@1.0.18/dist
```
#### Method 5: Use your own cdn
-Pass the files under packages/engine/dist and packages/(react|rax)-simulator-renderer/dist in the source code to your cdn provider
+Pass the files under packages/engine/dist and packages/react-simulator-renderer/dist in the source code to your cdn provider
## 🔗 Related Links
diff --git a/packages/engine/package.json b/packages/engine/package.json
index 4caf32c00..7eef6514b 100644
--- a/packages/engine/package.json
+++ b/packages/engine/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-engine",
- "version": "1.2.5",
+ "version": "1.3.0",
"description": "An enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系",
"main": "lib/engine-core.js",
"module": "es/engine-core.js",
@@ -19,15 +19,15 @@
"license": "MIT",
"dependencies": {
"@alifd/next": "^1.19.12",
- "@alilc/lowcode-designer": "1.2.5",
- "@alilc/lowcode-editor-core": "1.2.5",
- "@alilc/lowcode-editor-skeleton": "1.2.5",
+ "@alilc/lowcode-designer": "1.3.0",
+ "@alilc/lowcode-editor-core": "1.3.0",
+ "@alilc/lowcode-editor-skeleton": "1.3.0",
"@alilc/lowcode-engine-ext": "^1.0.0",
- "@alilc/lowcode-plugin-designer": "1.2.5",
- "@alilc/lowcode-plugin-outline-pane": "1.2.5",
- "@alilc/lowcode-shell": "1.2.5",
- "@alilc/lowcode-utils": "1.2.5",
- "@alilc/lowcode-workspace": "1.2.5",
+ "@alilc/lowcode-plugin-designer": "1.3.0",
+ "@alilc/lowcode-plugin-outline-pane": "1.3.0",
+ "@alilc/lowcode-shell": "1.3.0",
+ "@alilc/lowcode-utils": "1.3.0",
+ "@alilc/lowcode-workspace": "1.3.0",
"react": "^16.8.1",
"react-dom": "^16.8.1"
},
diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts
index 3ac4c32a7..3ccfdf51d 100644
--- a/packages/engine/src/engine-core.ts
+++ b/packages/engine/src/engine-core.ts
@@ -51,6 +51,7 @@ import {
Canvas,
Workspace,
Config,
+ CommonUI,
} from '@alilc/lowcode-shell';
import { isPlainObject } from '@alilc/lowcode-utils';
import './modules/live-editing';
@@ -61,6 +62,7 @@ import { setterRegistry } from './inner-plugins/setter-registry';
import { defaultPanelRegistry } from './inner-plugins/default-panel-registry';
import { shellModelFactory } from './modules/shell-model-factory';
import { builtinHotkey } from './inner-plugins/builtin-hotkey';
+import { defaultContextMenu } from './inner-plugins/default-context-menu';
import { OutlinePlugin } from '@alilc/lowcode-plugin-outline-pane';
export * from './modules/skeleton-types';
@@ -77,6 +79,7 @@ async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins
await plugins.register(defaultPanelRegistryPlugin);
await plugins.register(builtinHotkey);
await plugins.register(registerDefaults, {}, { autoInit: true });
+ await plugins.register(defaultContextMenu);
return () => {
plugins.delete(OutlinePlugin.pluginName);
@@ -85,6 +88,7 @@ async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins
plugins.delete(defaultPanelRegistryPlugin.pluginName);
plugins.delete(builtinHotkey.pluginName);
plugins.delete(registerDefaults.pluginName);
+ plugins.delete(defaultContextMenu.pluginName);
};
}
@@ -111,6 +115,7 @@ const innerSetters = new InnerSetters();
const setters = new Setters(innerSetters);
const material = new Material(editor);
+const commonUI = new CommonUI(editor);
editor.set('project', project);
editor.set('setters' as any, setters);
editor.set('material', material);
@@ -138,8 +143,10 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
context.plugins = plugins;
context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` });
context.workspace = workspace;
+ context.commonUI = commonUI;
context.registerLevel = IPublicEnumPluginRegisterLevel.Default;
context.isPluginRegisteredInWorkspace = false;
+ editor.set('pluginContext', context);
},
};
@@ -161,6 +168,7 @@ export {
common,
workspace,
canvas,
+ commonUI,
};
// declare this is open-source version
export const isOpenSource = true;
diff --git a/packages/engine/src/inner-plugins/default-context-menu.ts b/packages/engine/src/inner-plugins/default-context-menu.ts
new file mode 100644
index 000000000..81978d920
--- /dev/null
+++ b/packages/engine/src/inner-plugins/default-context-menu.ts
@@ -0,0 +1,223 @@
+import {
+ IPublicEnumContextMenuType,
+ IPublicEnumDragObjectType,
+ IPublicEnumTransformStage,
+ IPublicModelNode,
+ IPublicModelPluginContext,
+ IPublicTypeDragNodeDataObject,
+ IPublicTypeNodeSchema,
+} from '@alilc/lowcode-types';
+import { isProjectSchema } from '@alilc/lowcode-utils';
+import { Message } from '@alifd/next';
+import { intl } from '../locale';
+
+function getNodesSchema(nodes: IPublicModelNode[]) {
+ const componentsTree = nodes.map((node) => node?.exportSchema(IPublicEnumTransformStage.Clone));
+ const data = { type: 'nodeSchema', componentsMap: {}, componentsTree };
+ return data;
+}
+
+async function getClipboardText(): Promise