feat: add some features

This commit is contained in:
LeoYuan 袁力皓 2022-03-30 15:08:26 +08:00
parent d086267990
commit 18d1a4fe1d
31 changed files with 324 additions and 43 deletions

View File

@ -1098,7 +1098,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// fix target : 浏览器事件响应目标
if (!e.target || notMyEvent) {
e.target = this.contentDocument?.elementFromPoint(e.canvasX!, e.canvasY!);
if (!isNaN(e.canvasX!) && !isNaN(e.canvasY!)) {
e.target = this.contentDocument?.elementFromPoint(e.canvasX!, e.canvasY!);
}
}
// 事件已订正
@ -1156,14 +1158,12 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return null;
}
const dropContainer = this.getDropContainer(e);
const canDropIn = dropContainer?.container?.componentMeta?.prototype?.options?.canDropIn;
const childWhitelist = dropContainer?.container?.componentMeta?.childWhitelist;
const lockedNode = getClosestNode(dropContainer?.container as Node, (node) => node.isLocked);
if (lockedNode) return null;
if (
!dropContainer ||
canDropIn === false ||
// too dirty
(nodes && typeof canDropIn === 'function' && !canDropIn(operationalNodes[0]))
(nodes && typeof childWhitelist === 'function' && !childWhitelist(operationalNodes[0]))
) {
return null;
}

View File

@ -226,13 +226,11 @@ export class SettingTopEntry implements SettingEntry {
}
// ==== compatibles for vision =====
getProp(propName: string | number) {
return this.get(propName);
}
// ==== copy some Node api =====
// `VE.Node.getProps`
getStatus() {
}

View File

@ -642,6 +642,7 @@ export class DocumentModel {
this.rootNodeVisitorMap[visitorName] = visitorResult;
} catch (e) {
console.error('RootNodeVisitor is not valid.');
console.error(e);
}
return visitorResult;
}

View File

@ -316,6 +316,7 @@ export class Prop implements IPropParent {
const slotSchema: SlotSchema = {
componentName: 'Slot',
title: data.title,
id: data.id,
name: data.name,
params: data.params,
children: data.value,

View File

@ -21,6 +21,7 @@ import {
PreferenceValueType,
IPluginPreferenceMananger,
} from './plugin-types';
import { isValidPreferenceKey } from './plugin-utils';
export default class PluginContext implements ILowCodePluginContext {
private readonly [editorSymbol]: Editor;
@ -53,26 +54,22 @@ export default class PluginContext implements ILowCodePluginContext {
this.plugins = plugins;
this.event = new Event(editor, { prefix: 'common' });
this.logger = getLogger({ level: 'warn', bizName: `designer:plugin:${pluginName}` });
const enhancePluginContextHook = engineConfig.get('enhancePluginContextHook');
if (enhancePluginContextHook) {
enhancePluginContextHook(this);
}
}
setPreference(
pluginName: string,
preferenceDeclaration: ILowCodePluginPreferenceDeclaration,
): void {
const isValidPreferenceKey = (key: string): boolean => {
if (!preferenceDeclaration || !Array.isArray(preferenceDeclaration.properties)) {
return false;
}
return preferenceDeclaration.properties.some((prop) => {
return prop.key === key;
});
};
const getPreferenceValue = (
key: string,
defaultValue?: PreferenceValueType,
): PreferenceValueType | undefined => {
if (!isValidPreferenceKey(key)) {
if (!isValidPreferenceKey(key, preferenceDeclaration)) {
return undefined;
}
const pluginPreference = this.plugins.getPluginPreference(pluginName) || {};

View File

@ -11,7 +11,9 @@ import {
ILowCodePluginConfigMeta,
PluginPreference,
ILowCodePluginPreferenceDeclaration,
isLowCodeRegisterOptions,
} from './plugin-types';
import { filterValidOptions } from './plugin-utils';
import { LowCodePlugin } from './plugin';
import LowCodePluginContext from './plugin-context';
import { invariant } from '../utils';
@ -41,15 +43,30 @@ export class LowCodePluginManager implements ILowCodePluginManager {
return semverSatisfies(engineVersion, versionExp);
}
/**
* register a plugin
* @param pluginConfigCreator - a creator function which returns the plugin config
* @param options - the plugin options
* @param registerOptions - the plugin register options
*/
async register(
pluginConfigCreator: (ctx: ILowCodePluginContext) => ILowCodePluginConfig,
options?: ILowCodeRegisterOptions,
pluginConfigCreator: (ctx: ILowCodePluginContext, options: any) => ILowCodePluginConfig,
options?: any,
registerOptions?: ILowCodeRegisterOptions,
): Promise<void> {
const { pluginName, meta = {} } = pluginConfigCreator as any;
// registerOptions maybe in the second place
if (isLowCodeRegisterOptions(options)) {
registerOptions = options;
options = {};
}
let { pluginName, meta = {} } = pluginConfigCreator as any;
const { preferenceDeclaration, engines } = meta as ILowCodePluginConfigMeta;
const ctx = this._getLowCodePluginContext({ pluginName });
const config = pluginConfigCreator(ctx);
const customFilterValidOptions = engineConfig.get('customPluginFilterOptions', filterValidOptions);
const config = pluginConfigCreator(ctx, customFilterValidOptions(options, preferenceDeclaration!));
// compat the legacy way to declare pluginName
// @ts-ignore
pluginName = pluginName || config.name;
invariant(
pluginName,
'pluginConfigCreator.pluginName required',
@ -58,7 +75,7 @@ export class LowCodePluginManager implements ILowCodePluginManager {
ctx.setPreference(pluginName, (preferenceDeclaration as ILowCodePluginPreferenceDeclaration));
const allowOverride = options?.override === true;
const allowOverride = registerOptions?.override === true;
if (this.pluginsMap.has(pluginName)) {
if (!allowOverride) {
@ -83,7 +100,8 @@ export class LowCodePluginManager implements ILowCodePluginManager {
}
const plugin = new LowCodePlugin(pluginName, this, config, meta);
if (options?.autoInit) {
// support initialization of those plugins which registered after normal initialization by plugin-manager
if (registerOptions?.autoInit) {
await plugin.init();
}
this.plugins.push(plugin);

View File

@ -48,6 +48,7 @@ export interface ILowCodePluginPreferenceDeclaration {
export type PluginPreference = Map<string, Record<string, PreferenceValueType>>;
export interface ILowCodePluginConfig {
dep?: string | string[];
init?(): void;
destroy?(): void;
exports?(): any;
@ -130,6 +131,10 @@ export interface ILowCodePluginManagerCore {
export type ILowCodePluginManager = ILowCodePluginManagerCore & ILowCodePluginManagerPluginAccessor;
export function isLowCodeRegisterOptions(opts: any): opts is ILowCodeRegisterOptions {
return opts && ('autoInit' in opts || 'override' in opts);
}
export interface ILowCodeRegisterOptions {
autoInit?: boolean;
// allow overriding existing plugin with same name when override === true

View File

@ -0,0 +1,28 @@
import type { ILowCodePluginPreferenceDeclaration } from './plugin-types';
import { isPlainObject } from 'lodash';
export function isValidPreferenceKey(
key: string,
preferenceDeclaration: ILowCodePluginPreferenceDeclaration,
): boolean {
if (!preferenceDeclaration || !Array.isArray(preferenceDeclaration.properties)) {
return false;
}
return preferenceDeclaration.properties.some((prop) => {
return prop.key === key;
});
}
export function filterValidOptions(opts: any, preferenceDeclaration: ILowCodePluginPreferenceDeclaration) {
if (!opts || !isPlainObject(opts)) return opts;
const filteredOpts = {} as any;
Object.keys(opts).forEach(key => {
if (isValidPreferenceKey(key, preferenceDeclaration)) {
const v = opts[key];
if (v !== undefined && v !== null) {
filteredOpts[key] = v;
}
}
});
return filteredOpts;
}

View File

@ -50,7 +50,11 @@ export class LowCodePlugin implements ILowCodePlugin {
if (typeof this.meta.dependencies === 'string') {
return [this.meta.dependencies];
}
return this.meta.dependencies || [];
// compat legacy way to declare dependencies
if (typeof this.config.dep === 'string') {
return [this.config.dep];
}
return this.meta.dependencies || this.config.dep || [];
}
get disabled() {

View File

@ -11,6 +11,7 @@ import {
RemoteComponentDescription,
GlobalEvent,
} from '@alilc/lowcode-types';
import { engineConfig } from './config';
import { globalLocale } from './intl';
import * as utils from './utils';
import Preference from './utils/preference';
@ -71,6 +72,8 @@ export class Editor extends (EventEmitter as any) implements IEditor {
this.setAssets(data);
return;
}
// store the data to engineConfig while invoking editor.set()
engineConfig.set(key as any, data);
this.context.set(key, data);
this.notifyGot(key);
}

View File

@ -314,6 +314,7 @@ body {
display: none;
flex-shrink: 0;
position: relative;
z-index: 820;
&.lc-area-visible {
display: block;
}

View File

@ -6,6 +6,7 @@ import {
LowCodePluginManager,
ILowCodePluginContext,
PluginPreference,
TransformStage,
} from '@alilc/lowcode-designer';
import {
Skeleton as InnerSkeleton,
@ -22,7 +23,8 @@ import utils from './modules/utils';
import * as editorCabin from './modules/editor-cabin';
import getSkeletonCabin from './modules/skeleton-cabin';
import getDesignerCabin from './modules/designer-cabin';
import classes from './modules/classes';
import symbols from './modules/symbols';
export * from './modules/editor-types';
export * from './modules/skeleton-types';
export * from './modules/designer-types';
@ -60,8 +62,12 @@ const config = engineConfig;
const event = new Event(editor, { prefix: 'common' });
const logger = getLogger({ level: 'warn', bizName: 'common' });
const designerCabin = getDesignerCabin(editor);
const objects = {
TransformStage,
};
const common = {
utils,
objects,
editorCabin,
designerCabin,
skeletonCabin,
@ -81,6 +87,12 @@ export {
// 兼容原 editor 的事件功能
event as editor,
};
// declare this is open-source version
export const isOpenSource = true;
export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
symbols,
classes,
};
// 注册一批内置插件
(async function registerPlugins() {

View File

@ -0,0 +1,25 @@
import {
Project,
Skeleton,
DocumentModel,
Node,
NodeChildren,
History,
SettingPropEntry,
SettingTopEntry,
Selection,
} from '@alilc/lowcode-shell';
import { Node as InnerNode } from '@alilc/lowcode-designer';
export default {
Project,
Skeleton,
DocumentModel,
Node,
NodeChildren,
History,
SettingPropEntry,
SettingTopEntry,
InnerNode,
Selection,
};

View File

@ -3,6 +3,10 @@ import {
isSettingField,
Designer,
TransformStage,
LiveEditing,
isDragNodeDataObject,
DragObjectType,
isNode,
} from '@alilc/lowcode-designer';
import { Editor } from '@alilc/lowcode-editor-core';
import { Dragon } from '@alilc/lowcode-shell';
@ -15,5 +19,9 @@ export default function getDesignerCabin(editor: Editor) {
isSettingField,
dragon: Dragon.create(designer.dragon),
TransformStage,
LiveEditing,
DragObjectType,
isDragNodeDataObject,
isNode,
};
}

View File

@ -13,3 +13,6 @@ export type ILowCodePluginConfig = designerCabin.ILowCodePluginConfig;
export type ILowCodePluginManager = designerCabin.ILowCodePluginManager;
export type ILowCodePluginContext = designerCabin.ILowCodePluginContext;
export type PluginPreference = designerCabin.PluginPreference;
export type PropsReducerContext = designerCabin.PropsReducerContext;
export type DragObjectType = designerCabin.DragObjectType;
export type DragNodeDataObject = designerCabin.DragNodeDataObject;

View File

@ -3,12 +3,11 @@ export {
Tip,
shallowIntl,
createIntl,
intl,
createSetterContent,
// TODO: To be deleted
obx,
untracked,
computed,
observer,
getSetter,
getSettersMap,
globalLocale,
} from '@alilc/lowcode-editor-core';

View File

@ -0,0 +1,21 @@
import {
projectSymbol,
documentSymbol,
nodeSymbol,
designerSymbol,
skeletonSymbol,
editorSymbol,
settingPropEntrySymbol,
settingTopEntrySymbol,
} from '@alilc/lowcode-shell';
export default {
projectSymbol,
documentSymbol,
nodeSymbol,
skeletonSymbol,
editorSymbol,
designerSymbol,
settingPropEntrySymbol,
settingTopEntrySymbol,
};

View File

@ -106,7 +106,11 @@ export default class TreeTitle extends Component<{
data-id={treeNode.id}
onClick={() => {
if (isModal) {
node.document.modalNodesManager.setVisible(node);
if (node.getVisible()) {
node.document.modalNodesManager.setInvisible(node);
} else {
node.document.modalNodesManager.setVisible(node);
}
return;
}
if (node.conditionGroup) {

View File

@ -260,6 +260,10 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
this.initOnVisibleChangeEvent(_leaf);
this.curEventLeaf = _leaf;
cache.ref.set(props.componentId, {
makeUnitRender: this.makeUnitRender,
});
let cacheState = cache.state.get(props.componentId);
if (!cacheState || cacheState.__tag !== props.__tag) {
cacheState = this.defaultState;
@ -557,7 +561,6 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
<LeafHoc
{...props}
forwardedRef={ref}
ref={(_ref: any) => cache.ref.set(props.componentId, _ref)}
/>
));

View File

@ -1,8 +1,10 @@
import {
ComponentMeta as InnerComponentMeta,
ParentalNode,
} from '@alilc/lowcode-designer';
import { componentMetaSymbol } from './symbols';
import Node from './node';
import { NodeData, NodeSchema } from '@alilc/lowcode-types';
import { componentMetaSymbol, nodeSymbol } from './symbols';
export default class ComponentMeta {
private readonly [componentMetaSymbol]: InnerComponentMeta;
@ -75,6 +77,13 @@ export default class ComponentMeta {
return this[componentMetaSymbol].npm;
}
/**
* @deprecated
*/
get prototype() {
return this[componentMetaSymbol].prototype;
}
/**
* npm
* @param npm
@ -90,4 +99,26 @@ export default class ComponentMeta {
getMetadata() {
return this[componentMetaSymbol].getMetadata();
}
/**
* check if the current node could be placed in parent node
* @param my
* @param parent
* @returns
*/
checkNestingUp(my: Node | NodeData, parent: ParentalNode<NodeSchema>) {
const curNode = my.isNode ? my[nodeSymbol] : my;
return this[componentMetaSymbol].checkNestingUp(curNode as any, parent);
}
/**
* check if the target node(s) could be placed in current node
* @param my
* @param parent
* @returns
*/
checkNestingDown(my: Node | NodeData, target: NodeSchema | Node | NodeSchema[]) {
const curNode = my.isNode ? my[nodeSymbol] : my;
return this[componentMetaSymbol].checkNestingDown(curNode as any, target[nodeSymbol] || target);
}
}

View File

@ -6,7 +6,13 @@ import {
IOnChangeOptions as InnerIOnChangeOptions,
PropChangeOptions as InnerPropChangeOptions,
} from '@alilc/lowcode-designer';
import { TransformStage, RootSchema, NodeSchema, NodeData, GlobalEvent } from '@alilc/lowcode-types';
import {
TransformStage,
RootSchema,
NodeSchema,
NodeData,
GlobalEvent,
} from '@alilc/lowcode-types';
import Node from './node';
import Selection from './selection';
import Detecting from './detecting';
@ -33,6 +39,7 @@ type PropChangeOptions = {
export default class DocumentModel {
private readonly [documentSymbol]: InnerDocumentModel;
private readonly [editorSymbol]: Editor;
private _focusNode: Node;
public selection: Selection;
public detecting: Detecting;
public history: History;
@ -45,6 +52,8 @@ export default class DocumentModel {
this.detecting = new Detecting(document);
this.history = new History(document.getHistory());
this.canvas = new Canvas(document.designer);
this._focusNode = Node.create(this[documentSymbol].focusNode);
}
static create(document: InnerDocumentModel | undefined | null) {
@ -68,6 +77,14 @@ export default class DocumentModel {
return Node.create(this[documentSymbol].getRoot());
}
get focusNode(): Node {
return this._focusNode;
}
set focusNode(node: Node) {
this._focusNode = node;
}
/**
*
* @returns
@ -87,6 +104,11 @@ export default class DocumentModel {
return ModalNodesManager.create(this[documentSymbol].modalNodesManager);
}
// @TODO: 不能直接暴露
get dropLocation() {
return this[documentSymbol].dropLocation;
}
/**
* nodeId Node
* @param nodeId
@ -157,7 +179,7 @@ export default class DocumentModel {
* document
*/
onAddNode(fn: (node: Node) => void) {
this[documentSymbol].onNodeCreate((node: InnerNode) => {
return this[documentSymbol].onNodeCreate((node: InnerNode) => {
fn(Node.create(node)!);
});
}
@ -166,7 +188,7 @@ export default class DocumentModel {
* document
*/
onRemoveNode(fn: (node: Node) => void) {
this[documentSymbol].onNodeDestroy((node: InnerNode) => {
return this[documentSymbol].onNodeDestroy((node: InnerNode) => {
fn(Node.create(node)!);
});
}

View File

@ -16,6 +16,13 @@ export default class Dragon {
return new Dragon(dragon);
}
/**
* is dragging or not
*/
get dragging() {
return this[dragonSymbol].dragging;
}
/**
* dragstart
* @param func

View File

@ -61,6 +61,15 @@ export default class Event {
}
this[editorSymbol].emit(`${this.options.prefix}:${event}`, ...args);
}
/**
* DO NOT USE if u fully understand what this method does.
* @param event
* @param args
*/
__internalEmit__(event: string, ...args: unknown[]) {
this[editorSymbol].emit(event, ...args);
}
}
export function getEvent(editor: InnerEditor, options: any = { prefix: 'common' }) {

View File

@ -5,6 +5,7 @@ import Event, { getEvent } from './event';
import History from './history';
import Material from './material';
import Node from './node';
import NodeChildren from './node-children';
import Project from './project';
import Prop from './prop';
import Selection from './selection';
@ -13,6 +14,7 @@ import Hotkey from './hotkey';
import Skeleton from './skeleton';
import Dragon from './dragon';
import SettingPropEntry from './setting-prop-entry';
import SettingTopEntry from './setting-top-entry';
export * from './symbols';
/**
@ -30,6 +32,7 @@ export {
History,
Material,
Node,
NodeChildren,
Project,
Prop,
Selection,
@ -37,6 +40,7 @@ export {
Hotkey,
Skeleton,
SettingPropEntry,
SettingTopEntry,
Dragon,
getEvent,
};

View File

@ -7,9 +7,10 @@ import {
addBuiltinComponentAction,
removeBuiltinComponentAction,
modifyBuiltinComponentAction,
isComponentMeta,
} from '@alilc/lowcode-designer';
import { AssetsJson } from '@alilc/lowcode-utils';
import { ComponentAction } from '@alilc/lowcode-types';
import { ComponentAction, ComponentMetadata } from '@alilc/lowcode-types';
import { editorSymbol, designerSymbol } from './symbols';
import ComponentMeta from './component-meta';
@ -86,6 +87,26 @@ export default class Material {
return ComponentMeta.create(this[designerSymbol].getComponentMeta(componentName));
}
/**
* create an instance of ComponentMeta by given metadata
* @param metadata
* @returns
*/
createComponentMeta(metadata: ComponentMetadata) {
return ComponentMeta.create(this[designerSymbol].createComponentMeta(metadata));
}
/**
* test if the given object is a ComponentMeta instance or not
* @param obj
* @returns
*/
isComponentMeta(obj: any) {
return isComponentMeta(obj);
}
/**
*
* @returns

View File

@ -37,6 +37,13 @@ export default class NodeChildren {
return this[nodeChildrenSymbol].isEmpty();
}
/**
* judge if it is not empty
*/
get notEmpty() {
return !this.isEmpty;
}
/**
*
* @param node
@ -81,7 +88,7 @@ export default class NodeChildren {
* @returns
*/
get(index: number) {
return this[nodeChildrenSymbol].get(index);
return Node.create(this[nodeChildrenSymbol].get(index));
}
/**

View File

@ -17,9 +17,13 @@ export default class Node {
private readonly [documentSymbol]: InnerDocumentModel;
private readonly [nodeSymbol]: InnerNode;
private _id: string;
constructor(node: InnerNode) {
this[nodeSymbol] = node;
this[documentSymbol] = node.document;
this._id = this[nodeSymbol].id;
}
static create(node: InnerNode | null | undefined) {
@ -36,7 +40,14 @@ export default class Node {
* id
*/
get id() {
return this[nodeSymbol].id;
return this._id;
}
/**
* set id
*/
set id(id: string) {
this._id = id;
}
/**
@ -84,7 +95,7 @@ export default class Node {
/**
*
*/
get isModal() {
get isModal() {
return this[nodeSymbol].isModal();
}
@ -109,6 +120,13 @@ export default class Node {
return this[nodeSymbol].isLeaf();
}
/**
* judge if it is a node or not
*/
get isNode() {
return true;
}
/**
*
*/
@ -208,10 +226,17 @@ export default class Node {
/**
*
*/
get propsData() {
get propsData() {
return this[nodeSymbol].propsData;
}
/**
* - schema
*/
get schema(): any {
return this[nodeSymbol].schema;
}
/**
* @deprecated use .children instead
*/
@ -258,6 +283,14 @@ export default class Node {
return this[nodeSymbol].hasLoop();
}
getVisible() {
return this[nodeSymbol].getVisible();
}
isConditionalVisible() {
return this[nodeSymbol].isConditionalVisible();
}
/**
* @deprecated use .props instead
*/
@ -265,6 +298,10 @@ export default class Node {
return this.props;
}
contains(node: Node) {
return this[nodeSymbol].contains(node[nodeSymbol]);
}
/**
* path
* @param path a / a.b / a.0

View File

@ -43,6 +43,11 @@ export default class Prop {
return Node.create(this[propSymbol].getNode());
}
/**
* judge if it is a prop or not
*/
get isProp() { return true; }
/**
*
* @param val

View File

@ -22,6 +22,13 @@ export default class Selection {
return this[selectionSymbol].selected;
}
/**
* return selected Node instance
*/
get node() {
return this.getNodes()[0];
}
/**
*
* @param id

View File

@ -13,7 +13,7 @@
},
"dependencies": {
"@alilc/lowcode-datasource-types": "^1.0.0",
"react": "^16.9 || ^17",
"react": "^16.9",
"strict-event-emitter-types": "^2.0.0"
},
"devDependencies": {

View File

@ -102,6 +102,6 @@ export function invariant(check: any, message: string, thing?: any) {
export function deprecate(fail: any, message: string, alterative?: string) {
if (fail) {
console.warn(`Deprecation: ${message}` + (alterative ? `, use ${alterative} instead.'` : ''));
console.warn(`Deprecation: ${message}` + (alterative ? `, use ${alterative} instead.` : ''));
}
}