mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-11 18:42:56 +00:00
feat(context-menu): update context-menu docs, details, styles
This commit is contained in:
parent
3e7d199a7c
commit
bb5d7ddf82
@ -48,8 +48,70 @@ CommonUI API 是一个专为低代码引擎设计的组件 UI 库,使用它开
|
|||||||
| condition | 显示条件函数<br/>Function to determine display condition | (nodes: IPublicModelNode[]) => boolean (optional) | |
|
| condition | 显示条件函数<br/>Function to determine display condition | (nodes: IPublicModelNode[]) => boolean (optional) | |
|
||||||
| disabled | 禁用条件函数,可选<br/>Function to determine disabled condition, optional | (nodes: IPublicModelNode[]) => boolean (optional) | |
|
| disabled | 禁用条件函数,可选<br/>Function to determine disabled condition, optional | (nodes: IPublicModelNode[]) => boolean (optional) | |
|
||||||
|
|
||||||
|
**ContextMenu 示例**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const App = () => {
|
||||||
|
const menuItems: IPublicTypeContextMenuAction[] = [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: '选项 1',
|
||||||
|
action: () => console.log('选项 1 被点击'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: '选项 2',
|
||||||
|
action: () => console.log('选项 2 被点击'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ContextMenu = ctx.commonUI.ContextMenu;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ContextMenu menus={menuItems}>
|
||||||
|
<div>右键点击这里</div>
|
||||||
|
</ContextMenu>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
```
|
||||||
|
|
||||||
|
**ContextMenu.create 示例**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const App = () => {
|
||||||
|
const menuItems: IPublicTypeContextMenuAction[] = [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: '选项 1',
|
||||||
|
action: () => console.log('选项 1 被点击'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: '选项 2',
|
||||||
|
action: () => console.log('选项 2 被点击'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ContextMenu = ctx.commonUI.ContextMenu;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div onClick={(e) => {
|
||||||
|
ContextMenu.create(menuItems, e);
|
||||||
|
}}>点击这里</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
```
|
||||||
|
|
||||||
### Balloon
|
### Balloon
|
||||||
|
|
||||||
详细文档: [Balloon Documentation](https://fusion.design/pc/component/balloon)
|
详细文档: [Balloon Documentation](https://fusion.design/pc/component/balloon)
|
||||||
|
|
||||||
### Breadcrumb
|
### Breadcrumb
|
||||||
|
|||||||
@ -250,6 +250,33 @@ material.modifyBuiltinComponentAction('remove', (action) => {
|
|||||||
addContextMenuOption(action: IPublicTypeContextMenuAction): void;
|
addContextMenuOption(action: IPublicTypeContextMenuAction): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
示例
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { IPublicEnumContextMenuType } from '@alilc/lowcode-types';
|
||||||
|
|
||||||
|
material.addContextMenuOption({
|
||||||
|
name: 'parentItem',
|
||||||
|
title: 'Parent Item',
|
||||||
|
condition: (nodes) => true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'childItem1',
|
||||||
|
title: 'Child Item 1',
|
||||||
|
action: (nodes) => console.log('Child Item 1 clicked', nodes),
|
||||||
|
condition: (nodes) => true
|
||||||
|
},
|
||||||
|
// 分割线
|
||||||
|
{
|
||||||
|
type: IPublicEnumContextMenuType.SEPARATOR
|
||||||
|
name: 'separator.1'
|
||||||
|
}
|
||||||
|
// 更多子菜单项...
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
#### removeContextMenuOption
|
#### removeContextMenuOption
|
||||||
|
|
||||||
删除特定右键菜单项
|
删除特定右键菜单项
|
||||||
@ -274,7 +301,26 @@ removeContextMenuOption(name: string): void;
|
|||||||
adjustContextMenuLayout(fn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[]): void;
|
adjustContextMenuLayout(fn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[]): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**示例**
|
||||||
|
|
||||||
|
通过 adjustContextMenuLayout 补充分割线
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
material.adjustContextMenuLayout((actions: IPublicTypeContextMenuAction) => {
|
||||||
|
const names = ['a', 'b'];
|
||||||
|
const newActions = [];
|
||||||
|
actions.forEach(d => {
|
||||||
|
newActions.push(d);
|
||||||
|
if (names.include(d.name)) {
|
||||||
|
newActions.push({ type: 'separator' })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newActions
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
### 物料元数据
|
### 物料元数据
|
||||||
|
|
||||||
#### getComponentMeta
|
#### getComponentMeta
|
||||||
获取指定名称的物料元数据
|
获取指定名称的物料元数据
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { IPublicTypeContextMenuAction, IPublicEnumContextMenuType, IPublicTypeContextMenuItem, IPublicApiMaterial } from '@alilc/lowcode-types';
|
import { IPublicTypeContextMenuAction, IPublicEnumContextMenuType, IPublicTypeContextMenuItem, IPublicApiMaterial, IPublicModelPluginContext } from '@alilc/lowcode-types';
|
||||||
import { IDesigner, INode } from './designer';
|
import { IDesigner, INode } from './designer';
|
||||||
import { createContextMenu, parseContextMenuAsReactNode, parseContextMenuProperties, uniqueId } from '@alilc/lowcode-utils';
|
import { createContextMenu, parseContextMenuAsReactNode, parseContextMenuProperties, uniqueId } from '@alilc/lowcode-utils';
|
||||||
import { Menu } from '@alifd/next';
|
import { Menu } from '@alifd/next';
|
||||||
@ -48,6 +48,7 @@ export class GlobalContextMenuActions {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const actions: IPublicTypeContextMenuAction[] = [];
|
const actions: IPublicTypeContextMenuAction[] = [];
|
||||||
|
let contextMenu: ContextMenuActions = this.contextMenuActionsMap.values().next().value;
|
||||||
this.contextMenuActionsMap.forEach((contextMenu) => {
|
this.contextMenuActionsMap.forEach((contextMenu) => {
|
||||||
actions.push(...contextMenu.actions);
|
actions.push(...contextMenu.actions);
|
||||||
});
|
});
|
||||||
@ -57,11 +58,13 @@ export class GlobalContextMenuActions {
|
|||||||
const destroy = () => {
|
const destroy = () => {
|
||||||
destroyFn?.();
|
destroyFn?.();
|
||||||
};
|
};
|
||||||
|
const pluginContext: IPublicModelPluginContext = contextMenu.designer.editor.get('pluginContext') as IPublicModelPluginContext;
|
||||||
|
|
||||||
const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
|
const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
|
||||||
nodes: [],
|
nodes: [],
|
||||||
destroy,
|
destroy,
|
||||||
event,
|
event,
|
||||||
|
pluginContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!menus.length) {
|
if (!menus.length) {
|
||||||
@ -73,6 +76,7 @@ export class GlobalContextMenuActions {
|
|||||||
const menuNode = parseContextMenuAsReactNode(layoutMenu, {
|
const menuNode = parseContextMenuAsReactNode(layoutMenu, {
|
||||||
destroy,
|
destroy,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
|
pluginContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
@ -160,10 +164,13 @@ export class ContextMenuActions implements IContextMenuActions {
|
|||||||
destroyFn?.();
|
destroyFn?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pluginContext: IPublicModelPluginContext = this.designer.editor.get('pluginContext') as IPublicModelPluginContext;
|
||||||
|
|
||||||
const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
|
const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
|
||||||
nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
|
nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
|
||||||
destroy,
|
destroy,
|
||||||
event,
|
event,
|
||||||
|
pluginContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!menus.length) {
|
if (!menus.length) {
|
||||||
@ -175,7 +182,7 @@ export class ContextMenuActions implements IContextMenuActions {
|
|||||||
const menuNode = parseContextMenuAsReactNode(layoutMenu, {
|
const menuNode = parseContextMenuAsReactNode(layoutMenu, {
|
||||||
destroy,
|
destroy,
|
||||||
nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
|
nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
|
||||||
designer,
|
pluginContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
destroyFn = createContextMenu(menuNode, {
|
destroyFn = createContextMenu(menuNode, {
|
||||||
|
|||||||
@ -115,12 +115,11 @@ const innerSetters = new InnerSetters();
|
|||||||
const setters = new Setters(innerSetters);
|
const setters = new Setters(innerSetters);
|
||||||
|
|
||||||
const material = new Material(editor);
|
const material = new Material(editor);
|
||||||
const commonUI = new CommonUI();
|
const commonUI = new CommonUI(editor);
|
||||||
editor.set('project', project);
|
editor.set('project', project);
|
||||||
editor.set('setters' as any, setters);
|
editor.set('setters' as any, setters);
|
||||||
editor.set('material', material);
|
editor.set('material', material);
|
||||||
editor.set('innerHotkey', innerHotkey);
|
editor.set('innerHotkey', innerHotkey);
|
||||||
editor.set('commonUI' as any, commonUI);
|
|
||||||
const config = new Config(engineConfig);
|
const config = new Config(engineConfig);
|
||||||
const event = new Event(commonEvent, { prefix: 'common' });
|
const event = new Event(commonEvent, { prefix: 'common' });
|
||||||
const logger = new Logger({ level: 'warn', bizName: 'common' });
|
const logger = new Logger({ level: 'warn', bizName: 'common' });
|
||||||
@ -147,6 +146,7 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
|
|||||||
context.commonUI = commonUI;
|
context.commonUI = commonUI;
|
||||||
context.registerLevel = IPublicEnumPluginRegisterLevel.Default;
|
context.registerLevel = IPublicEnumPluginRegisterLevel.Default;
|
||||||
context.isPluginRegisteredInWorkspace = false;
|
context.isPluginRegisteredInWorkspace = false;
|
||||||
|
editor.set('pluginContext', context);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,11 @@ import {
|
|||||||
IPublicModelNode,
|
IPublicModelNode,
|
||||||
IPublicModelPluginContext,
|
IPublicModelPluginContext,
|
||||||
IPublicTypeDragNodeDataObject,
|
IPublicTypeDragNodeDataObject,
|
||||||
IPublicTypeI18nData,
|
|
||||||
IPublicTypeNodeSchema,
|
IPublicTypeNodeSchema,
|
||||||
} from '@alilc/lowcode-types';
|
} from '@alilc/lowcode-types';
|
||||||
import { isI18nData, isProjectSchema } from '@alilc/lowcode-utils';
|
import { isProjectSchema } from '@alilc/lowcode-utils';
|
||||||
import { Notification } from '@alifd/next';
|
import { Notification } from '@alifd/next';
|
||||||
import { intl, getLocale } from '../locale';
|
import { intl } from '../locale';
|
||||||
|
|
||||||
function getNodesSchema(nodes: IPublicModelNode[]) {
|
function getNodesSchema(nodes: IPublicModelNode[]) {
|
||||||
const componentsTree = nodes.map((node) => node?.exportSchema(IPublicEnumTransformStage.Clone));
|
const componentsTree = nodes.map((node) => node?.exportSchema(IPublicEnumTransformStage.Clone));
|
||||||
@ -18,15 +17,6 @@ function getNodesSchema(nodes: IPublicModelNode[]) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIntlStr(data: string | IPublicTypeI18nData) {
|
|
||||||
if (!isI18nData(data)) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const locale = getLocale();
|
|
||||||
return data[locale] || data['zh-CN'] || data['zh_CN'] || data['en-US'] || data['en_US'] || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getClipboardText(): Promise<IPublicTypeNodeSchema[]> {
|
async function getClipboardText(): Promise<IPublicTypeNodeSchema[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// 使用 Clipboard API 读取剪贴板内容
|
// 使用 Clipboard API 读取剪贴板内容
|
||||||
@ -61,8 +51,9 @@ async function getClipboardText(): Promise<IPublicTypeNodeSchema[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const defaultContextMenu = (ctx: IPublicModelPluginContext) => {
|
export const defaultContextMenu = (ctx: IPublicModelPluginContext) => {
|
||||||
const { material, canvas } = ctx;
|
const { material, canvas, common } = ctx;
|
||||||
const { clipboard } = canvas;
|
const { clipboard } = canvas;
|
||||||
|
const { intl: utilsIntl } = common.utils;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init() {
|
init() {
|
||||||
@ -150,7 +141,7 @@ export const defaultContextMenu = (ctx: IPublicModelPluginContext) => {
|
|||||||
});
|
});
|
||||||
if (canAddNodes.length === 0) {
|
if (canAddNodes.length === 0) {
|
||||||
Notification.open({
|
Notification.open({
|
||||||
content: `${nodeSchema.map(d => getIntlStr(d.title || d.componentName)).join(',')}等组件无法放置到${getIntlStr(parent.title || parent.componentName as any)}内`,
|
content: `${nodeSchema.map(d => utilsIntl(d.title || d.componentName)).join(',')}等组件无法放置到${utilsIntl(parent.title || parent.componentName as any)}内`,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -198,7 +189,7 @@ export const defaultContextMenu = (ctx: IPublicModelPluginContext) => {
|
|||||||
});
|
});
|
||||||
if (canAddNodes.length === 0) {
|
if (canAddNodes.length === 0) {
|
||||||
Notification.open({
|
Notification.open({
|
||||||
content: `${nodeSchema.map(d => getIntlStr(d.title || d.componentName)).join(',')}等组件无法放置到${getIntlStr(node.title || node.componentName as any)}内`,
|
content: `${nodeSchema.map(d => utilsIntl(d.title || d.componentName)).join(',')}等组件无法放置到${utilsIntl(node.title || node.componentName as any)}内`,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import { IPublicApiCommonUI } from '@alilc/lowcode-types';
|
import { IPublicApiCommonUI, IPublicModelPluginContext, IPublicTypeContextMenuAction } from '@alilc/lowcode-types';
|
||||||
import {
|
import {
|
||||||
|
IEditor,
|
||||||
Tip as InnerTip,
|
Tip as InnerTip,
|
||||||
Title as InnerTitle,
|
Title as InnerTitle,
|
||||||
} from '@alilc/lowcode-editor-core';
|
} from '@alilc/lowcode-editor-core';
|
||||||
import { Balloon, Breadcrumb, Button, Card, Checkbox, DatePicker, Dialog, Dropdown, Form, Icon, Input, Loading, Message, Overlay, Pagination, Radio, Search, Select, SplitButton, Step, Switch, Tab, Table, Tree, TreeSelect, Upload, Divider } from '@alifd/next';
|
import { Balloon, Breadcrumb, Button, Card, Checkbox, DatePicker, Dialog, Dropdown, Form, Icon, Input, Loading, Message, Overlay, Pagination, Radio, Search, Select, SplitButton, Step, Switch, Tab, Table, Tree, TreeSelect, Upload, Divider } from '@alifd/next';
|
||||||
import { ContextMenu } from '../components/context-menu';
|
import { ContextMenu } from '../components/context-menu';
|
||||||
|
import { editorSymbol } from '../symbols';
|
||||||
|
|
||||||
export class CommonUI implements IPublicApiCommonUI {
|
export class CommonUI implements IPublicApiCommonUI {
|
||||||
|
[editorSymbol]: IEditor;
|
||||||
|
|
||||||
Balloon = Balloon;
|
Balloon = Balloon;
|
||||||
Breadcrumb = Breadcrumb;
|
Breadcrumb = Breadcrumb;
|
||||||
Button = Button;
|
Button = Button;
|
||||||
@ -35,13 +39,29 @@ export class CommonUI implements IPublicApiCommonUI {
|
|||||||
Upload = Upload;
|
Upload = Upload;
|
||||||
Divider = Divider;
|
Divider = Divider;
|
||||||
|
|
||||||
|
constructor(editor: IEditor) {
|
||||||
|
this[editorSymbol] = editor;
|
||||||
|
}
|
||||||
|
|
||||||
get Tip() {
|
get Tip() {
|
||||||
return InnerTip;
|
return InnerTip;
|
||||||
}
|
}
|
||||||
get Title() {
|
get Title() {
|
||||||
return InnerTitle;
|
return InnerTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
get ContextMenu() {
|
get ContextMenu() {
|
||||||
return ContextMenu;
|
const editor = this[editorSymbol];
|
||||||
|
const innerContextMenu = (props: any) => {
|
||||||
|
const pluginContext: IPublicModelPluginContext = editor.get('pluginContext') as IPublicModelPluginContext;
|
||||||
|
return <ContextMenu {...props} pluginContext={pluginContext} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
innerContextMenu.create = (menus: IPublicTypeContextMenuAction[], event: MouseEvent) => {
|
||||||
|
const pluginContext: IPublicModelPluginContext = editor.get('pluginContext') as IPublicModelPluginContext;
|
||||||
|
return ContextMenu.create(pluginContext, menus, event);
|
||||||
|
};
|
||||||
|
|
||||||
|
return innerContextMenu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,11 +1,12 @@
|
|||||||
import { createContextMenu, parseContextMenuAsReactNode, parseContextMenuProperties } from '@alilc/lowcode-utils';
|
import { createContextMenu, parseContextMenuAsReactNode, parseContextMenuProperties } from '@alilc/lowcode-utils';
|
||||||
import { engineConfig } from '@alilc/lowcode-editor-core';
|
import { engineConfig } from '@alilc/lowcode-editor-core';
|
||||||
import { IPublicTypeContextMenuAction } from '@alilc/lowcode-types';
|
import { IPublicModelPluginContext, IPublicTypeContextMenuAction } from '@alilc/lowcode-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export function ContextMenu({ children, menus }: {
|
export function ContextMenu({ children, menus, pluginContext }: {
|
||||||
menus: IPublicTypeContextMenuAction[];
|
menus: IPublicTypeContextMenuAction[];
|
||||||
children: React.ReactElement[] | React.ReactElement;
|
children: React.ReactElement[] | React.ReactElement;
|
||||||
|
pluginContext: IPublicModelPluginContext;
|
||||||
}): React.ReactElement<any, string | React.JSXElementConstructor<any>> {
|
}): React.ReactElement<any, string | React.JSXElementConstructor<any>> {
|
||||||
if (!engineConfig.get('enableContextMenu')) {
|
if (!engineConfig.get('enableContextMenu')) {
|
||||||
return (
|
return (
|
||||||
@ -23,7 +24,10 @@ export function ContextMenu({ children, menus }: {
|
|||||||
};
|
};
|
||||||
const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, {
|
const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, {
|
||||||
destroy,
|
destroy,
|
||||||
}));
|
pluginContext,
|
||||||
|
}), {
|
||||||
|
pluginContext,
|
||||||
|
});
|
||||||
|
|
||||||
if (!children?.length) {
|
if (!children?.length) {
|
||||||
return;
|
return;
|
||||||
@ -44,4 +48,20 @@ export function ContextMenu({ children, menus }: {
|
|||||||
return (
|
return (
|
||||||
<>{childrenWithContextMenu}</>
|
<>{childrenWithContextMenu}</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContextMenu.create = (pluginContext: IPublicModelPluginContext, menus: IPublicTypeContextMenuAction[], event: MouseEvent) => {
|
||||||
|
const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, {
|
||||||
|
pluginContext,
|
||||||
|
}), {
|
||||||
|
pluginContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!children?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createContextMenu(children, {
|
||||||
|
event,
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,32 +1,42 @@
|
|||||||
.context-menu-tree-wrap {
|
.engine-context-menu-tree-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 4px 10px 4px 32px;
|
padding: 4px 10px 4px 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-tree-children {
|
.engine-context-menu-tree-children {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-tree-bg {
|
.engine-context-menu-item {
|
||||||
position: absolute;
|
.engine-context-menu-text {
|
||||||
left: 0;
|
color: var(--color-text);
|
||||||
right: 0;
|
}
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-menu-tree-bg-inner {
|
|
||||||
position: absolute;
|
|
||||||
height: 24px;
|
|
||||||
top: -24px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-block-background-light);
|
.engine-context-menu-text {
|
||||||
|
color: var(--color-title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disbale {
|
||||||
|
.engine-context-menu-text {
|
||||||
|
color: var(--color-text-disabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-tree-selected-icon {
|
.engine-context-menu-title {
|
||||||
|
color: var(--color-text);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-block-background-light);
|
||||||
|
color: var(--color-title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-context-menu-tree-selecte-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
color: var(--color-icon-active);
|
color: var(--color-icon-active);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Menu, Icon } from '@alifd/next';
|
import { Menu, Icon } from '@alifd/next';
|
||||||
import { IDesigner } from '@alilc/lowcode-designer';
|
import { IPublicEnumContextMenuType, IPublicModelNode, IPublicModelPluginContext, IPublicTypeContextMenuAction, IPublicTypeContextMenuItem } from '@alilc/lowcode-types';
|
||||||
import { IPublicEnumContextMenuType, IPublicModelNode, IPublicTypeContextMenuAction, IPublicTypeContextMenuItem } from '@alilc/lowcode-types';
|
|
||||||
import { Logger } from '@alilc/lowcode-utils';
|
import { Logger } from '@alilc/lowcode-utils';
|
||||||
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './context-menu.scss';
|
import './context-menu.scss';
|
||||||
|
|
||||||
@ -10,43 +10,51 @@ const { Item, Divider, PopupItem } = Menu;
|
|||||||
|
|
||||||
const MAX_LEVEL = 2;
|
const MAX_LEVEL = 2;
|
||||||
|
|
||||||
|
interface IOptions {
|
||||||
|
nodes?: IPublicModelNode[] | null;
|
||||||
|
destroy?: Function;
|
||||||
|
pluginContext: IPublicModelPluginContext;
|
||||||
|
}
|
||||||
|
|
||||||
const Tree = (props: {
|
const Tree = (props: {
|
||||||
node?: IPublicModelNode;
|
node?: IPublicModelNode | null;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
options: {
|
options: IOptions;
|
||||||
nodes?: IPublicModelNode[] | null;
|
|
||||||
destroy?: Function;
|
|
||||||
designer?: IDesigner;
|
|
||||||
};
|
|
||||||
}) => {
|
}) => {
|
||||||
const { node } = props;
|
const { node } = props;
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return (
|
return (
|
||||||
<div className="context-menu-tree-wrap">{ props.children }</div>
|
<div className="engine-context-menu-tree-wrap">{ props.children }</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const commonUI = props.options.designer?.editor?.get('commonUI');
|
const { common } = props.options.pluginContext || {};
|
||||||
|
const { intl } = common?.utils || {};
|
||||||
const Title = commonUI?.Title;
|
const indent = node.zLevel * 8 + 32;
|
||||||
|
const style = {
|
||||||
|
paddingLeft: indent,
|
||||||
|
marginLeft: -indent,
|
||||||
|
marginRight: -10,
|
||||||
|
paddingRight: 10,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tree {...props} node={node.parent} >
|
<Tree {...props} node={node.parent} >
|
||||||
{props.options.nodes?.[0].id === node.id ? (<Icon className="context-menu-tree-selected-icon" size="small" type="success" />) : null}
|
|
||||||
<Title title={node.title} />
|
|
||||||
<div
|
<div
|
||||||
className="context-menu-tree-children"
|
className="engine-context-menu-title"
|
||||||
|
onClick={() => {
|
||||||
|
props.options.destroy?.();
|
||||||
|
node.select();
|
||||||
|
}}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
{props.options.nodes?.[0].id === node.id ? (<Icon className="engine-context-menu-tree-selecte-icon" size="small" type="success" />) : null}
|
||||||
|
{intl(node.title)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="engine-context-menu-tree-children"
|
||||||
>
|
>
|
||||||
<div
|
|
||||||
className="context-menu-tree-bg"
|
|
||||||
onClick={() => {
|
|
||||||
props.options.destroy?.();
|
|
||||||
node.select();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="context-menu-tree-bg-inner" />
|
|
||||||
</div>
|
|
||||||
{ props.children }
|
{ props.children }
|
||||||
</div>
|
</div>
|
||||||
</Tree>
|
</Tree>
|
||||||
@ -55,11 +63,10 @@ const Tree = (props: {
|
|||||||
|
|
||||||
let destroyFn: Function | undefined;
|
let destroyFn: Function | undefined;
|
||||||
|
|
||||||
export function parseContextMenuAsReactNode(menus: IPublicTypeContextMenuItem[], options: {
|
export function parseContextMenuAsReactNode(menus: IPublicTypeContextMenuItem[], options: IOptions): React.ReactNode[] {
|
||||||
nodes?: IPublicModelNode[] | null;
|
const { common } = options.pluginContext || {};
|
||||||
destroy?: Function;
|
const { intl = (title: any) => title } = common?.utils || {};
|
||||||
designer?: IDesigner;
|
|
||||||
} = {}): React.ReactNode[] {
|
|
||||||
const children: React.ReactNode[] = [];
|
const children: React.ReactNode[] = [];
|
||||||
menus.forEach((menu, index) => {
|
menus.forEach((menu, index) => {
|
||||||
if (menu.type === IPublicEnumContextMenuType.SEPARATOR) {
|
if (menu.type === IPublicEnumContextMenuType.SEPARATOR) {
|
||||||
@ -70,14 +77,33 @@ export function parseContextMenuAsReactNode(menus: IPublicTypeContextMenuItem[],
|
|||||||
if (menu.type === IPublicEnumContextMenuType.MENU_ITEM) {
|
if (menu.type === IPublicEnumContextMenuType.MENU_ITEM) {
|
||||||
if (menu.items && menu.items.length) {
|
if (menu.items && menu.items.length) {
|
||||||
children.push((
|
children.push((
|
||||||
<PopupItem key={menu.name} label={menu.title}>
|
<PopupItem
|
||||||
|
className={classNames('engine-context-menu-item', {
|
||||||
|
disbale: menu.disabled,
|
||||||
|
})}
|
||||||
|
key={menu.name}
|
||||||
|
label={<div className="engine-context-menu-text">{intl(menu.title)}</div>}
|
||||||
|
>
|
||||||
<Menu className="next-context engine-context-menu">
|
<Menu className="next-context engine-context-menu">
|
||||||
{ parseContextMenuAsReactNode(menu.items, options) }
|
{ parseContextMenuAsReactNode(menu.items, options) }
|
||||||
</Menu>
|
</Menu>
|
||||||
</PopupItem>
|
</PopupItem>
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
children.push((<Item disabled={menu.disabled} onClick={menu.action} key={menu.name}>{menu.title}</Item>));
|
children.push((
|
||||||
|
<Item
|
||||||
|
className={classNames('engine-context-menu-item', {
|
||||||
|
disbale: menu.disabled,
|
||||||
|
})}
|
||||||
|
disabled={menu.disabled}
|
||||||
|
onClick={menu.action}
|
||||||
|
key={menu.name}
|
||||||
|
>
|
||||||
|
<div className="engine-context-menu-text">
|
||||||
|
{intl(menu.title)}
|
||||||
|
</div>
|
||||||
|
</Item>
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,9 +117,7 @@ export function parseContextMenuAsReactNode(menus: IPublicTypeContextMenuItem[],
|
|||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseContextMenuProperties(menus: (IPublicTypeContextMenuAction | Omit<IPublicTypeContextMenuAction, 'items'>)[], options: {
|
export function parseContextMenuProperties(menus: (IPublicTypeContextMenuAction | Omit<IPublicTypeContextMenuAction, 'items'>)[], options: IOptions & {
|
||||||
nodes?: IPublicModelNode[] | null;
|
|
||||||
destroy?: Function;
|
|
||||||
event?: MouseEvent;
|
event?: MouseEvent;
|
||||||
}, level = 1): IPublicTypeContextMenuItem[] {
|
}, level = 1): IPublicTypeContextMenuItem[] {
|
||||||
destroyFn?.();
|
destroyFn?.();
|
||||||
@ -156,7 +180,6 @@ function getMenuItemHeight() {
|
|||||||
}
|
}
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const styles = getComputedStyle(root);
|
const styles = getComputedStyle(root);
|
||||||
// Access the value of the CSS variable
|
|
||||||
const menuItemHeight = styles.getPropertyValue('--context-menu-item-height').trim();
|
const menuItemHeight = styles.getPropertyValue('--context-menu-item-height').trim();
|
||||||
cachedMenuItemHeight = menuItemHeight;
|
cachedMenuItemHeight = menuItemHeight;
|
||||||
|
|
||||||
|
|||||||
@ -128,13 +128,12 @@ export class BasicContext implements IBasicContext {
|
|||||||
const logger = getLogger({ level: 'warn', bizName: 'common' });
|
const logger = getLogger({ level: 'warn', bizName: 'common' });
|
||||||
const skeleton = new Skeleton(innerSkeleton, 'any', true);
|
const skeleton = new Skeleton(innerSkeleton, 'any', true);
|
||||||
const canvas = new Canvas(editor, true);
|
const canvas = new Canvas(editor, true);
|
||||||
const commonUI = new CommonUI();
|
const commonUI = new CommonUI(editor);
|
||||||
editor.set('setters', setters);
|
editor.set('setters', setters);
|
||||||
editor.set('project', project);
|
editor.set('project', project);
|
||||||
editor.set('material', material);
|
editor.set('material', material);
|
||||||
editor.set('hotkey', hotkey);
|
editor.set('hotkey', hotkey);
|
||||||
editor.set('innerHotkey', innerHotkey);
|
editor.set('innerHotkey', innerHotkey);
|
||||||
editor.set('commonUI' as any, commonUI);
|
|
||||||
this.innerSetters = innerSetters;
|
this.innerSetters = innerSetters;
|
||||||
this.innerSkeleton = innerSkeleton;
|
this.innerSkeleton = innerSkeleton;
|
||||||
this.skeleton = skeleton;
|
this.skeleton = skeleton;
|
||||||
@ -175,6 +174,7 @@ export class BasicContext implements IBasicContext {
|
|||||||
}
|
}
|
||||||
context.registerLevel = registerLevel;
|
context.registerLevel = registerLevel;
|
||||||
context.isPluginRegisteredInWorkspace = registerLevel === IPublicEnumPluginRegisterLevel.Workspace;
|
context.isPluginRegisteredInWorkspace = registerLevel === IPublicEnumPluginRegisterLevel.Workspace;
|
||||||
|
editor.set('pluginContext', context);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user