mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 01:21:58 +00:00
Merge branch 'polyfill/vision' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into polyfill/vision
This commit is contained in:
commit
38599fffa1
@ -41,7 +41,8 @@
|
||||
"@alife/theme-lowcode-light": "^0.1.0",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1",
|
||||
"@ali/vu-function-parser": "^2.5.0-beta.0"
|
||||
"@ali/vu-function-parser": "^2.5.0-beta.0",
|
||||
"compare-versions": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ali/iceluna-cli": "^0.0.16",
|
||||
|
||||
@ -18,8 +18,11 @@ import { upgradeAssetsBundle } from './upgrade-assets';
|
||||
import { isCSSUrl } from '@ali/lowcode-utils';
|
||||
import { I18nSetter } from '@ali/visualengine-utils';
|
||||
import VariableSetter from '@ali/vs-variable-setter';
|
||||
import { isObject, isArray } from 'lodash';
|
||||
import _isArray from "lodash/isArray";
|
||||
import _isObject from "lodash/isObject";
|
||||
import _get from 'lodash/get';
|
||||
import funcParser from '@ali/vu-function-parser';
|
||||
import cv from 'compare-versions';
|
||||
|
||||
|
||||
const { editor, skeleton, context, HOOKS, Trunk } = Engine;
|
||||
@ -45,11 +48,7 @@ async function loadAssets() {
|
||||
|
||||
if (assets.packages) {
|
||||
assets.packages.forEach((item: any) => {
|
||||
if (item.package.indexOf('@ali/vc-') === 0 && item.urls) {
|
||||
item.urls = item.urls.filter((url: string) => {
|
||||
return url.indexOf('view.mobile') < 0;
|
||||
});
|
||||
} else if (item.package && externals.indexOf(item.package) > -1) {
|
||||
if (item.package && externals.indexOf(item.package) > -1) {
|
||||
item.urls = null;
|
||||
}
|
||||
});
|
||||
@ -103,7 +102,7 @@ function initDemoPanes() {
|
||||
props: {
|
||||
align: 'bottom',
|
||||
icon: 'set',
|
||||
description: '设置',
|
||||
description: '设置'
|
||||
},
|
||||
});
|
||||
skeleton.add({
|
||||
@ -113,7 +112,7 @@ function initDemoPanes() {
|
||||
props: {
|
||||
align: 'bottom',
|
||||
icon: 'help',
|
||||
description: '帮助',
|
||||
description: '帮助'
|
||||
},
|
||||
});
|
||||
|
||||
@ -336,9 +335,9 @@ function replaceFuncProp(props?: any){
|
||||
}
|
||||
if ((prop.compiled && prop.source) || prop.type === 'actionRef' || prop.type === 'js') {
|
||||
replaceProps[name] = funcParser(prop);
|
||||
} else if (isObject(prop)) {
|
||||
} else if (_isObject(prop)) {
|
||||
replaceFuncProp(prop);
|
||||
} else if (isArray(prop)) {
|
||||
} else if (_isArray(prop)) {
|
||||
prop.map((propItem) => {
|
||||
replaceFuncProp(propItem);
|
||||
});
|
||||
@ -348,7 +347,6 @@ function replaceFuncProp(props?: any){
|
||||
for (const name in replaceProps) {
|
||||
props[name] = replaceProps[name];
|
||||
}
|
||||
|
||||
return props;
|
||||
};
|
||||
|
||||
@ -399,7 +397,7 @@ function initHistoryPane() {
|
||||
historyManager: {
|
||||
historyManager,
|
||||
app: {
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
index: -940,
|
||||
|
||||
1
packages/demo/src/vision/module.d.ts
vendored
1
packages/demo/src/vision/module.d.ts
vendored
@ -10,3 +10,4 @@ declare module '@ali/ve-i18n-manage-pane';
|
||||
declare module '@ali/ve-action-pane';
|
||||
declare module '@ali/vu-legao-design-fetch-context';
|
||||
declare module "@ali/vu-function-parser";
|
||||
declare module "compare-versions";
|
||||
|
||||
@ -39,27 +39,25 @@ export class BorderHoveringInstance extends PureComponent<{
|
||||
}
|
||||
|
||||
@observer
|
||||
export class BorderHovering extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
export class BorderHovering extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@computed get scale() {
|
||||
return (this.context as BuiltinSimulatorHost).viewport.scale;
|
||||
return this.props.host.viewport.scale;
|
||||
}
|
||||
|
||||
@computed get scrollX() {
|
||||
return (this.context as BuiltinSimulatorHost).viewport.scrollX;
|
||||
return this.props.host.viewport.scrollX;
|
||||
}
|
||||
|
||||
@computed get scrollY() {
|
||||
return (this.context as BuiltinSimulatorHost).viewport.scrollY;
|
||||
return this.props.host.viewport.scrollY;
|
||||
}
|
||||
|
||||
@computed get current() {
|
||||
const host = this.context as BuiltinSimulatorHost;
|
||||
const host = this.props.host;
|
||||
const doc = host.document;
|
||||
const selection = doc.selection;
|
||||
const current = host.designer.hovering.current;
|
||||
@ -70,7 +68,7 @@ export class BorderHovering extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const host = this.context as BuiltinSimulatorHost;
|
||||
const host = this.props.host;
|
||||
const current = this.current;
|
||||
if (!current || host.viewport.scrolling) {
|
||||
return <Fragment />;
|
||||
|
||||
@ -132,11 +132,9 @@ function createAction(content: ReactNode | ComponentType<any> | ActionContentObj
|
||||
}
|
||||
|
||||
@observer
|
||||
export class BorderSelectingForNode extends Component<{ node: Node }> {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
export class BorderSelectingForNode extends Component<{ host: BuiltinSimulatorHost; node: Node }> {
|
||||
get host(): BuiltinSimulatorHost {
|
||||
return this.context;
|
||||
return this.props.host;
|
||||
}
|
||||
|
||||
get dragging(): boolean {
|
||||
@ -177,11 +175,9 @@ export class BorderSelectingForNode extends Component<{ node: Node }> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class BorderSelecting extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
get host(): BuiltinSimulatorHost {
|
||||
return this.context;
|
||||
return this.props.host;
|
||||
}
|
||||
|
||||
get dragging(): boolean {
|
||||
@ -211,7 +207,7 @@ export class BorderSelecting extends Component {
|
||||
return (
|
||||
<Fragment>
|
||||
{selecting.map((node) => (
|
||||
<BorderSelectingForNode key={node.id} node={node} />
|
||||
<BorderSelectingForNode key={node.id} host={this.props.host} node={node} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@ -25,6 +25,9 @@
|
||||
align-items: stretch;
|
||||
justify-content: flex-end;
|
||||
pointer-events: all;
|
||||
> * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-action {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Component } from 'react';
|
||||
import { observer } from '@ali/lowcode-editor-core';
|
||||
import { BorderHovering } from './border-hovering';
|
||||
import { SimulatorContext } from '../context';
|
||||
import { BuiltinSimulatorHost } from '../host';
|
||||
import { BorderSelecting } from './border-selecting';
|
||||
import BorderResizing from './border-resizing';
|
||||
@ -10,15 +9,13 @@ import './bem-tools.less';
|
||||
import './borders.less';
|
||||
|
||||
@observer
|
||||
export class BemTools extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const host = this.context as BuiltinSimulatorHost;
|
||||
const host = this.props.host;
|
||||
const { scrollX, scrollY, scale } = host.viewport;
|
||||
return (
|
||||
<div className="lc-bem-tools" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
|
||||
|
||||
@ -112,24 +112,19 @@ function processDetail({ target, detail, document }: DropLocation): InsertionDat
|
||||
}
|
||||
|
||||
@observer
|
||||
export class InsertionView extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
@computed get host(): BuiltinSimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
export class InsertionView extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const loc = this.host.document.dropLocation;
|
||||
const { host } = this.props;
|
||||
const loc = host.document.dropLocation;
|
||||
if (!loc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { scale, scrollX, scrollY } = this.host.viewport;
|
||||
const { scale, scrollX, scrollY } = host.viewport;
|
||||
const { edge, insertType, coverRect, nearRect, vertical } = processDetail(loc);
|
||||
|
||||
if (!edge) {
|
||||
|
||||
@ -41,20 +41,17 @@ export class BuiltinSimulatorHostView extends Component<SimulatorHostProps> {
|
||||
const { Provider } = SimulatorContext;
|
||||
return (
|
||||
<div className="lc-simulator">
|
||||
<Provider value={this.host}>
|
||||
{/*progressing.visible ? <PreLoaderView /> : null*/}
|
||||
<Canvas />
|
||||
</Provider>
|
||||
{/*progressing.visible ? <PreLoaderView /> : null*/}
|
||||
<Canvas host={this.host} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Canvas extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
class Canvas extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
render() {
|
||||
const sim = this.context as BuiltinSimulatorHost;
|
||||
const sim = this.props.host;
|
||||
let className = 'lc-simulator-canvas';
|
||||
if (sim.deviceClassName) {
|
||||
className += ` ${sim.deviceClassName}`;
|
||||
@ -65,8 +62,8 @@ class Canvas extends Component {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div ref={elmt => sim.mountViewport(elmt)} className="lc-simulator-canvas-viewport">
|
||||
<BemTools />
|
||||
<Content />
|
||||
<BemTools host={sim} />
|
||||
<Content host={sim} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -74,10 +71,9 @@ class Canvas extends Component {
|
||||
}
|
||||
|
||||
@observer
|
||||
class Content extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
class Content extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
render() {
|
||||
const sim = this.context as BuiltinSimulatorHost;
|
||||
const sim = this.props.host;
|
||||
const viewport = sim.viewport;
|
||||
let frameStyle = {};
|
||||
if (viewport.scale < 1) {
|
||||
|
||||
@ -7,6 +7,8 @@ import {
|
||||
TitleContent,
|
||||
TransformedComponentMetadata,
|
||||
NestingFilter,
|
||||
isTitleConfig,
|
||||
I18nData,
|
||||
} from '@ali/lowcode-types';
|
||||
import { computed } from '@ali/lowcode-editor-core';
|
||||
import { Node, ParentalNode } from './document';
|
||||
@ -17,6 +19,8 @@ import { IconPage } from './icons/page';
|
||||
import { IconComponent } from './icons/component';
|
||||
import { IconRemove } from './icons/remove';
|
||||
import { IconClone } from './icons/clone';
|
||||
import { ReactElement } from 'react';
|
||||
import { IconHidden } from './icons/hidden';
|
||||
|
||||
function ensureAList(list?: string | string[]): string[] | null {
|
||||
if (!list) {
|
||||
@ -91,12 +95,20 @@ export class ComponentMeta {
|
||||
private childWhitelist?: NestingFilter | null;
|
||||
|
||||
private _title?: TitleContent;
|
||||
get title() {
|
||||
get title(): string | I18nData | ReactElement {
|
||||
// TODO: 标记下。这块需要康师傅加一下API,页面正常渲染。
|
||||
// string | i18nData | ReactElement
|
||||
// TitleConfig title.label
|
||||
if (isTitleConfig(this._title)) {
|
||||
return (this._title.label as any) || this.componentName;
|
||||
}
|
||||
return this._title || this.componentName;
|
||||
}
|
||||
|
||||
@computed get icon() {
|
||||
// TODO: 标记下。这块需要康师傅加一下API,页面正常渲染。
|
||||
// give Slot default icon
|
||||
// if _title is TitleConfig get _title.icon
|
||||
return (
|
||||
this._transformedMetadata?.icon ||
|
||||
(this.componentName === 'Page' ? IconPage : this.isContainer ? IconContainer : IconComponent)
|
||||
@ -131,10 +143,10 @@ export class ComponentMeta {
|
||||
this._title =
|
||||
typeof title === 'string'
|
||||
? {
|
||||
type: 'i18n',
|
||||
'en-US': this.componentName,
|
||||
'zh-CN': title,
|
||||
}
|
||||
type: 'i18n',
|
||||
'en-US': this.componentName,
|
||||
'zh-CN': title,
|
||||
}
|
||||
: title;
|
||||
}
|
||||
|
||||
@ -319,6 +331,20 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
},
|
||||
important: true,
|
||||
},
|
||||
{
|
||||
name: 'hide',
|
||||
content: {
|
||||
icon: IconHidden,
|
||||
title: intlNode('hide'),
|
||||
action(node: Node) {
|
||||
node.getExtraProp('hidden', true)?.setValue(true);
|
||||
},
|
||||
},
|
||||
condition: (node: Node) => {
|
||||
return node.componentMeta.isModal;
|
||||
},
|
||||
important: true,
|
||||
},
|
||||
{
|
||||
name: 'copy',
|
||||
content: {
|
||||
@ -326,6 +352,8 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
title: intlNode('copy'),
|
||||
action(node: Node) {
|
||||
// node.remove();
|
||||
const { document: doc, parent, schema, index } = node;
|
||||
parent && doc.insertNode(parent, schema, index);
|
||||
},
|
||||
},
|
||||
important: true,
|
||||
|
||||
@ -4,6 +4,67 @@ import { focusing } from './focusing';
|
||||
import { insertChildren, TransformStage } from '../document';
|
||||
import clipboard from './clipboard';
|
||||
|
||||
function getNextForSelect(next: any, head?: any, parent?: any): any {
|
||||
if (next) {
|
||||
if (!head) {
|
||||
return next;
|
||||
}
|
||||
|
||||
let ret;
|
||||
if (next.isContainer()) {
|
||||
const children = next.getChildren() || [];
|
||||
if (children && !children.isEmpty()) {
|
||||
ret = getNextForSelect(children.get(0));
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = getNextForSelect(next.nextSibling);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
return getNextForSelect(parent.nextSibling, false, parent.getParent());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPrevForSelect(prev: any, head?: any, parent?: any): any {
|
||||
if (prev) {
|
||||
debugger;
|
||||
let ret;
|
||||
if (!head && prev.isContainer()) {
|
||||
const children = prev.getChildren() || [];
|
||||
const lastChild = children && !children.isEmpty() ? children.get(children.size - 1) : null;
|
||||
|
||||
ret = getPrevForSelect(lastChild);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!head) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
ret = getPrevForSelect(prev.prevSibling);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// hotkey binding
|
||||
hotkey.bind(['backspace', 'del'], (e: KeyboardEvent) => {
|
||||
const doc = focusing.focusDesigner?.currentDocument;
|
||||
@ -58,14 +119,15 @@ hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => {
|
||||
const data = { type: 'nodeSchema', componentsMap, componentsTree };
|
||||
|
||||
clipboard.setData(data);
|
||||
/*
|
||||
|
||||
const cutMode = action.indexOf('x') > 0;
|
||||
if (cutMode) {
|
||||
const parentNode = selected.getParent();
|
||||
parentNode.select();
|
||||
selected.remove();
|
||||
selected.forEach((node) => {
|
||||
const parentNode = node.getParent();
|
||||
parentNode?.select();
|
||||
node.remove();
|
||||
});
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
// command + v paste
|
||||
@ -111,3 +173,92 @@ hotkey.bind(['command+y', 'ctrl+y', 'command+shift+z'], (e) => {
|
||||
|
||||
his.forward();
|
||||
});
|
||||
|
||||
// sibling selection
|
||||
hotkey.bind(['left', 'right'], (e, action) => {
|
||||
const designer = focusing.focusDesigner;
|
||||
const doc = designer?.currentDocument;
|
||||
if (isFormEvent(e) || !doc) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
const selected = doc.selection.getTopNodes(true);
|
||||
if (!selected || selected.length < 1) {
|
||||
return;
|
||||
}
|
||||
const firstNode = selected[0];
|
||||
const silbing = action === 'left' ? firstNode?.prevSibling : firstNode?.nextSibling;
|
||||
silbing?.select();
|
||||
});
|
||||
|
||||
hotkey.bind(['up', 'down'], (e, action) => {
|
||||
const designer = focusing.focusDesigner;
|
||||
const doc = designer?.currentDocument;
|
||||
if (isFormEvent(e) || !doc) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
const selected = doc.selection.getTopNodes(true);
|
||||
if (!selected || selected.length < 1) {
|
||||
return;
|
||||
}
|
||||
const firstNode = selected[0];
|
||||
|
||||
if (action === 'down') {
|
||||
const next = getNextForSelect(firstNode, true, firstNode.getParent());
|
||||
next?.select();
|
||||
} else if (action === 'up') {
|
||||
const prev = getPrevForSelect(firstNode, true, firstNode.getParent());
|
||||
prev?.select();
|
||||
}
|
||||
});
|
||||
|
||||
hotkey.bind(['option+up', 'option+down', 'option+left', 'option+right'], (e, action) => {
|
||||
const designer = focusing.focusDesigner;
|
||||
const doc = designer?.currentDocument;
|
||||
if (isFormEvent(e) || !doc) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
const selected = doc.selection.getTopNodes(true);
|
||||
if (!selected || selected.length < 1) {
|
||||
return;
|
||||
}
|
||||
// TODO: 此处需要增加判断当前节点是否可被操作移动,原ve里是用 node.canOperating()来判断
|
||||
|
||||
const firstNode = selected[0];
|
||||
const parent = firstNode.getParent();
|
||||
if (!parent) return;
|
||||
|
||||
const isPrev = /(up|left)$/.test(action);
|
||||
const isTravel = /(up|down)$/.test(action);
|
||||
|
||||
const silbing = isPrev ? firstNode.prevSibling : firstNode.nextSibling;
|
||||
if (silbing) {
|
||||
if (isTravel && silbing.isContainer()) {
|
||||
const place = silbing.getSuitablePlace(firstNode, null);
|
||||
if (isPrev) {
|
||||
place.container.insertAfter(firstNode, place.ref);
|
||||
} else {
|
||||
place.container.insertBefore(firstNode, place.ref);
|
||||
}
|
||||
} else if (isPrev) {
|
||||
parent.insertBefore(firstNode, silbing);
|
||||
} else {
|
||||
parent.insertAfter(firstNode, silbing);
|
||||
}
|
||||
firstNode?.select();
|
||||
return;
|
||||
}
|
||||
if (isTravel) {
|
||||
const place = parent.getSuitablePlace(firstNode, null); // upwards
|
||||
if (place) {
|
||||
if (isPrev) {
|
||||
place.container.insertBefore(firstNode, place.ref);
|
||||
} else {
|
||||
place.container.insertAfter(firstNode, place.ref);
|
||||
}
|
||||
firstNode?.select();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -180,6 +180,11 @@ export class SettingPropEntry implements SettingEntry {
|
||||
return this.top;
|
||||
}
|
||||
|
||||
// add settingfield props
|
||||
get props() {
|
||||
return this.top;
|
||||
}
|
||||
|
||||
onValueChange(func: () => any) {
|
||||
this.emitter.on('valuechange', func);
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// all this file for polyfill vision logic
|
||||
|
||||
import { isValidElement } from 'react';
|
||||
import { isSetterConfig } from '@ali/lowcode-types';
|
||||
import { getSetter } from '@ali/lowcode-editor-core';
|
||||
|
||||
function getHotterFromSetter(setter) {
|
||||
return setter && (setter.Hotter || (setter.type && setter.type.Hotter)) || []; // eslint-disable-line
|
||||
@ -29,9 +31,9 @@ export class Transducer {
|
||||
constructor(context, config) {
|
||||
let { setter } = config;
|
||||
|
||||
// 1. validElement
|
||||
// 1. validElement
|
||||
// 2. SetterConfig
|
||||
// 3. SetterConfig[]
|
||||
// 3. SetterConfig[]
|
||||
if (Array.isArray(setter)) {
|
||||
setter = setter[0];
|
||||
} else if (isValidElement(setter) && setter.type.displayName === 'MixedSetter') {
|
||||
@ -40,6 +42,13 @@ export class Transducer {
|
||||
setter = setter.props.setters[0];
|
||||
}
|
||||
|
||||
if (isSetterConfig(setter)) {
|
||||
setter = setter.componentName;
|
||||
}
|
||||
if (typeof setter === 'string') {
|
||||
setter = getSetter(setter);
|
||||
}
|
||||
|
||||
this.setterTransducer = combineTransducer(
|
||||
getTransducerFromSetter(setter),
|
||||
getHotterFromSetter(setter),
|
||||
|
||||
@ -41,6 +41,7 @@ export class DocumentModel {
|
||||
private seqId = 0;
|
||||
private _simulator?: ISimulatorHost;
|
||||
|
||||
|
||||
/**
|
||||
* 模拟器
|
||||
*/
|
||||
@ -110,6 +111,14 @@ export class DocumentModel {
|
||||
}
|
||||
|
||||
readonly designer = this.project.designer;
|
||||
// getAddonData(name: string) {
|
||||
// const addon = this.addons.find((item) => item.name === name);
|
||||
// if (addon) {
|
||||
// return addon.exportData();
|
||||
// }
|
||||
// return this.addonsData[name];
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 生成唯一id
|
||||
@ -230,6 +239,14 @@ export class DocumentModel {
|
||||
this.selection.remove(node.id);
|
||||
node.remove();
|
||||
}
|
||||
getAddonData(name: string) {
|
||||
const addon = this.getNode(name)
|
||||
if (addon) {
|
||||
// 无法确定是否有这个api
|
||||
// return addon.exportData();
|
||||
}
|
||||
return addon
|
||||
}
|
||||
|
||||
@obx.ref private _dropLocation: DropLocation | null = null;
|
||||
/**
|
||||
@ -461,14 +478,16 @@ export class DocumentModel {
|
||||
return config.checkNestingDown(parent, obj) && this.checkNestingUp(parent, obj);
|
||||
}
|
||||
|
||||
// ======= compatibles
|
||||
// ======= compatibles for vision
|
||||
getRoot() {
|
||||
return this.rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容vision
|
||||
*/
|
||||
// add toData
|
||||
toData() {
|
||||
return { componentsTree: [this.project?.currentDocument?.export(TransformStage.Save)] };
|
||||
}
|
||||
|
||||
getHistory(): History {
|
||||
return this.history;
|
||||
}
|
||||
|
||||
@ -174,6 +174,10 @@ export class History {
|
||||
this.emitter.removeAllListeners();
|
||||
this.records = [];
|
||||
}
|
||||
|
||||
isModified() {
|
||||
return this.point !== this.session.cursor;
|
||||
}
|
||||
}
|
||||
|
||||
class Session {
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
PropsList,
|
||||
NodeData,
|
||||
TitleContent,
|
||||
I18nData,
|
||||
SlotSchema,
|
||||
PageSchema,
|
||||
ComponentSchema,
|
||||
@ -19,6 +20,7 @@ import { Prop } from './props/prop';
|
||||
import { ComponentMeta } from '../../component-meta';
|
||||
import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group';
|
||||
import { TransformStage } from './transform-stage';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
/**
|
||||
* 基础节点
|
||||
@ -122,7 +124,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@computed get title(): TitleContent {
|
||||
@computed get title(): string | I18nData | ReactElement {
|
||||
let t = this.getExtraProp('title');
|
||||
if (!t && this.componentMeta.descriptor) {
|
||||
t = this.getProp(this.componentMeta.descriptor, false);
|
||||
@ -136,6 +138,10 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
return this.componentMeta.title;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.componentMeta.icon;
|
||||
}
|
||||
|
||||
constructor(readonly document: DocumentModel, nodeSchema: Schema) {
|
||||
const { componentName, id, children, props, ...extras } = nodeSchema;
|
||||
this.id = id || `node$${document.nextId()}`;
|
||||
@ -154,8 +160,9 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
|
||||
private transformProps(props: any): any {
|
||||
// FIXME! support PropsList
|
||||
return this.document.designer.transformProps(props, this, TransformStage.Init);
|
||||
const x = this.document.designer.transformProps(props, this, TransformStage.Init);
|
||||
|
||||
return x;
|
||||
// TODO: run transducers in metadata.experimental
|
||||
}
|
||||
|
||||
@ -177,19 +184,19 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
return this.isParental() && this.componentMeta.isContainer;
|
||||
}
|
||||
|
||||
isRoot(): this is RootNode {
|
||||
isRoot(): boolean {
|
||||
return this.document.rootNode == (this as any);
|
||||
}
|
||||
|
||||
isPage(): this is PageNode {
|
||||
isPage(): boolean {
|
||||
return this.isRoot() && this.componentName === 'Page';
|
||||
}
|
||||
|
||||
isComponent(): this is ComponentNode {
|
||||
isComponent(): boolean {
|
||||
return this.isRoot() && this.componentName === 'Component';
|
||||
}
|
||||
|
||||
isSlot(): this is SlotNode {
|
||||
isSlot(): boolean {
|
||||
return this._slotFor != null && this.componentName === 'Slot';
|
||||
}
|
||||
|
||||
@ -219,8 +226,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isSlot() && this._parent) {
|
||||
this._parent.children.delete(this);
|
||||
if (this._parent) {
|
||||
if (this.isSlot()) {
|
||||
this._parent.removeSlot(this, false);
|
||||
} else {
|
||||
this._parent.children.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
this._parent = parent;
|
||||
@ -252,8 +263,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
* 移除当前节点
|
||||
*/
|
||||
remove() {
|
||||
if (!this.isSlot() && this.parent) {
|
||||
this.parent.children.delete(this, true);
|
||||
if (this.parent) {
|
||||
if (this.isSlot()) {
|
||||
this.parent.removeSlot(this, true);
|
||||
} else {
|
||||
this.parent.children.delete(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,24 +304,13 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
return this.props.export(TransformStage.Serilize).props || null;
|
||||
}
|
||||
|
||||
@obx.val _slots: Node[] = [];
|
||||
@computed hasSlots() {
|
||||
for (const item of this.props) {
|
||||
if (item.type === 'slot') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return this._slots.length > 0;
|
||||
}
|
||||
|
||||
@computed get slots() {
|
||||
// TODO: optimize recore/obx, array maked every time, donot as changed
|
||||
const slots: Node[] = [];
|
||||
this.props.forEach((item) => {
|
||||
if (item.type === 'slot') {
|
||||
slots.push(item.slotNode!);
|
||||
}
|
||||
});
|
||||
return slots;
|
||||
get slots() {
|
||||
return this._slots;
|
||||
}
|
||||
|
||||
@obx.ref private _conditionGroup: ExclusiveGroup | null = null;
|
||||
@ -352,7 +356,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
|
||||
@computed hasCondition() {
|
||||
const v = this.getExtraProp('condition', false)?.getValue();
|
||||
return v != null && v !== '';
|
||||
return v != null && v !== '' && v !== true;
|
||||
}
|
||||
|
||||
@computed hasLoop() {
|
||||
@ -536,6 +540,28 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
return comparePosition(this, otherNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个Slot节点
|
||||
*/
|
||||
removeSlot(slotNode: Node, purge = false): boolean {
|
||||
const i = this._slots.indexOf(slotNode);
|
||||
if (i < 0) {
|
||||
return false;
|
||||
}
|
||||
const deleted = this._slots.splice(i, 1)[0];
|
||||
if (purge) {
|
||||
// should set parent null
|
||||
deleted.internalSetParent(null);
|
||||
deleted.purge();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
addSlot(slotNode: Node) {
|
||||
slotNode.internalSetParent(this as ParentalNode);
|
||||
this._slots.push(slotNode);
|
||||
}
|
||||
|
||||
private purged = false;
|
||||
/**
|
||||
* 是否已销毁
|
||||
@ -679,9 +705,20 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
}
|
||||
|
||||
getRect(): DOMRect | null {
|
||||
if (this.isRoot()) {
|
||||
return this.document.simulator?.viewport.contentBounds || null;
|
||||
}
|
||||
return this.document.simulator?.computeRect(this) || null;
|
||||
}
|
||||
|
||||
getPrototype() {
|
||||
return this;
|
||||
}
|
||||
|
||||
getIcon() {
|
||||
return this.icon;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ export class Prop implements IPropParent {
|
||||
} else {
|
||||
const owner = this.props.owner;
|
||||
this._slotNode = owner.document.createNode<SlotNode>(slotSchema);
|
||||
this._slotNode.internalSetParent(owner as any);
|
||||
owner.addSlot(this._slotNode);
|
||||
this._slotNode.internalSetSlotFor(this);
|
||||
}
|
||||
this.dispose();
|
||||
|
||||
@ -309,4 +309,11 @@ export class Props implements IPropParent {
|
||||
getPropValue(path: string): any {
|
||||
return this.getProp(path, false)?.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单个属性值
|
||||
*/
|
||||
setPropValue(path: string, value: any) {
|
||||
this.getProp(path, true)!.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"copy": "Copy",
|
||||
"remove": "Remove",
|
||||
"hide": "Hide",
|
||||
"Condition Group": "Condition Group",
|
||||
"No opened document": "No opened document, open some document to editing"
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"copy": "复制",
|
||||
"remove": "删除",
|
||||
"hide": "隐藏",
|
||||
"Condition Group": "条件组",
|
||||
"No opened document": "没有打开的页面,请选择页面打开编辑"
|
||||
}
|
||||
|
||||
@ -49,13 +49,14 @@ class AliGlobalLocale {
|
||||
}
|
||||
} else if (g_config) {
|
||||
if (g_config.locale) {
|
||||
return languageMap[g_config.locale] || (g_config.locale || '').replace('_', '-');
|
||||
return languageMap[g_config.locale] || g_config.locale.replace('_', '-');
|
||||
}
|
||||
}
|
||||
|
||||
let locale: string = '';
|
||||
if (navigator.language) {
|
||||
locale = (navigator.language as string).replace('_', '-');
|
||||
const lang = (navigator.language as string);
|
||||
return languageMap[lang] || lang.replace('_', '-');
|
||||
}
|
||||
|
||||
// IE10 及更低版本使用 browserLanguage
|
||||
|
||||
@ -37,10 +37,13 @@ function injectVars(msg: string, params: any, locale: string): string {
|
||||
});*/
|
||||
}
|
||||
|
||||
export function intl(data: any, params?: object): string {
|
||||
export function intl(data: any, params?: object): ReactNode {
|
||||
if (!isI18nData(data)) {
|
||||
return data;
|
||||
}
|
||||
if (data.intl) {
|
||||
return data.intl;
|
||||
}
|
||||
const locale = globalLocale.getLocale();
|
||||
const tries = generateTryLocales(locale);
|
||||
let msg: string | undefined;
|
||||
|
||||
48
packages/editor-core/src/utils/focusing-track.ts
Normal file
48
packages/editor-core/src/utils/focusing-track.ts
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
class FocusingManager {
|
||||
deploy() {
|
||||
|
||||
}
|
||||
send(e: MouseEvent | KeyboardEvent) {
|
||||
|
||||
}
|
||||
addModalCheck() {
|
||||
|
||||
}
|
||||
create(config: FocusableConfig) {
|
||||
|
||||
}
|
||||
activeItem() {
|
||||
|
||||
}
|
||||
suspenceItem() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export interface FocusableConfig {
|
||||
range: HTMLElement | ((e: MouseEvent) => boolean);
|
||||
modal?: boolean;
|
||||
onEsc?: () => void;
|
||||
onBlur?: () => void;
|
||||
}
|
||||
|
||||
class Focusable {
|
||||
readonly isModal: boolean;
|
||||
constructor(private manager: FocusingManager, { range, modal }: FocusableConfig) {
|
||||
this.isModal = modal == null ? false : modal;
|
||||
|
||||
}
|
||||
checkRange(e: MouseEvent) {
|
||||
|
||||
}
|
||||
active() {
|
||||
this.manager.activeItem(this);
|
||||
}
|
||||
suspence() {
|
||||
this.manager.suspenceItem(this);
|
||||
}
|
||||
purge() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
.lc-field-icon {
|
||||
margin-right: @x-gap;
|
||||
// margin-right: @x-gap;
|
||||
transform-origin: center;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
&.lc-plain-field {
|
||||
// for top-level style
|
||||
padding: 8px 10px;
|
||||
// padding: 8px 10px;
|
||||
> .lc-field-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@ -34,7 +34,16 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// for top-level style
|
||||
padding: 8px 10px;
|
||||
padding: 16px;
|
||||
&:first-child{
|
||||
padding-top: 16px;
|
||||
}
|
||||
&:last-child{
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
&+.lc-inline-field{
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
> .lc-field-head {
|
||||
width: 70px;
|
||||
@ -58,19 +67,29 @@
|
||||
border-top: 1px solid var(--color-line-normal);
|
||||
}
|
||||
> .lc-field-head {
|
||||
padding-left: @x-gap;
|
||||
// padding-left: @x-gap;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
background: var(--color-block-background-shallow, rgba(31,56,88,.06));
|
||||
border-bottom: 1px solid var(--color-line-normal);
|
||||
color: var(--color-title);
|
||||
// background: var(--color-block-background-shallow, rgba(31,56,88,.06));
|
||||
// border-bottom: 1px solid var(--color-line-normal);
|
||||
// color: var(--color-title);
|
||||
padding: 0 16px;
|
||||
background-color: #F7F9FC;
|
||||
color: #8F9BB3;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
> .lc-field-body {
|
||||
padding: @y-gap @x-gap/2;
|
||||
// padding: @y-gap @x-gap/2;
|
||||
padding: 16px;
|
||||
.lc-inline-field{
|
||||
margin-bottom: 16px;
|
||||
&:last-child{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ .lc-inline-field {
|
||||
@ -94,7 +113,11 @@
|
||||
}
|
||||
|
||||
&.lc-accordion-field {
|
||||
position: relative;
|
||||
// collapsed
|
||||
&:last-child.lc-field-is-collapsed{
|
||||
border-bottom: 1px solid var(--color-line-normal);
|
||||
}
|
||||
&.lc-field-is-collapsed {
|
||||
> .lc-field-head .lc-field-icon {
|
||||
transform: rotate(180deg);
|
||||
@ -106,14 +129,15 @@
|
||||
|
||||
// 邻近的保持上下距离
|
||||
+ .lc-field {
|
||||
margin-top: @y-gap;
|
||||
// margin-top: @y-gap;
|
||||
}
|
||||
}
|
||||
|
||||
// 2rd level reset
|
||||
.lc-field-body {
|
||||
.lc-inline-field {
|
||||
padding: @y-gap @x-gap/2 0 @x-gap/2;
|
||||
// padding: @y-gap @x-gap/2 0 @x-gap/2;
|
||||
padding: 0;
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
@ -130,9 +154,10 @@
|
||||
> .lc-field-head {
|
||||
padding-left: @x-gap/2;
|
||||
background: var(--color-block-background-light);
|
||||
border-bottom-color: var(--color-line-light);
|
||||
border-bottom-color: var(--color-line-light, rgba(31, 56, 88, .1));
|
||||
> .lc-field-icon {
|
||||
margin-right: @x-gap/2;
|
||||
// margin-right: @x-gap/2;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,5 +171,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
>.lc-block-setter {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,9 +42,22 @@
|
||||
margin-right: 0;
|
||||
>.lc-setter-actions {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
right: 16px;
|
||||
top: 0;
|
||||
height: 32px;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lc-block-field > .lc-field-body > .lc-setter-mixed{
|
||||
|
||||
}
|
||||
.lc-accordion-field > .lc-field-body > .lc-setter-mixed{
|
||||
position: static;
|
||||
margin-right: 0;
|
||||
> .lc-setter-actions{
|
||||
right: 32px;
|
||||
top: 6px;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,13 +70,6 @@ export class SettingsMain {
|
||||
this._settings = this.designer.createSettingEntry(this.editor, nodes);
|
||||
}
|
||||
|
||||
onceOutlineVisible(fn: () => void): () => void {
|
||||
this.emitter.on('outline-visible', fn);
|
||||
return () => {
|
||||
this.emitter.removeListener('outline-visible', fn);
|
||||
};
|
||||
}
|
||||
|
||||
purge() {
|
||||
this.disposeListener();
|
||||
this.emitter.removeAllListeners();
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 5px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid var(--color-line-normal);
|
||||
.lc-settings-navigator-icon {
|
||||
width: 16px;
|
||||
|
||||
@ -12,6 +12,8 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
|
||||
}
|
||||
|
||||
private dispose?: () => void;
|
||||
// private focusing?: FocusingItem;
|
||||
private shell: HTMLElement | null = null;
|
||||
componentDidMount() {
|
||||
const { area } = this.props;
|
||||
const triggerClose = () => area.setVisible(false);
|
||||
@ -19,18 +21,44 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
|
||||
this.dispose = () => {
|
||||
area.skeleton.editor.removeListener('designer.dragstart', triggerClose);
|
||||
}
|
||||
|
||||
/*
|
||||
this.focusing = focusingTrack.create(this.shell!, {
|
||||
onEsc: () => {
|
||||
this.props.area.setVisible(false);
|
||||
},
|
||||
onBlur: () => {
|
||||
this.props.area.setVisible(false);
|
||||
},
|
||||
// modal: boolean
|
||||
});
|
||||
*/
|
||||
|
||||
this.onEffect();
|
||||
}
|
||||
|
||||
onEffect() {
|
||||
/*
|
||||
const { area } = this.props;
|
||||
if (area.visible) {
|
||||
this.focusing?.active();
|
||||
} else {
|
||||
this.focusing?.suspense();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.onEffect();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// this.focusing?.purge();
|
||||
this.dispose?.();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
// TODO: add focusingManager
|
||||
// focusin set focus (push|replace)
|
||||
// focusout remove focus
|
||||
// onEsc
|
||||
const width = area.current?.config.props?.width;
|
||||
const hideTitleBar = area.current?.config.props?.hideTitleBar;
|
||||
const style = width ? {
|
||||
@ -38,6 +66,7 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
|
||||
} : undefined;
|
||||
return (
|
||||
<div
|
||||
ref={(ref) => { this.shell = ref }}
|
||||
className={classNames('lc-left-float-pane', {
|
||||
'lc-area-visible': area.visible,
|
||||
})}
|
||||
|
||||
@ -13,7 +13,7 @@ export default class MainArea extends Component<{ area: Area<any, Panel | Widget
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<div className={classNames('lc-main-area')}>
|
||||
<div className={classNames('lc-main-area engine-workspacepane')}>
|
||||
{area.container.items.map((item) => item.content)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -12,7 +12,7 @@ export default class RightArea extends Component<{ area: Area<any, Panel> }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<div className={classNames('lc-right-area', {
|
||||
<div className={classNames('lc-right-area engine-tabpane', {
|
||||
'lc-area-visible': area.visible,
|
||||
})}>
|
||||
<Contents area={area} />
|
||||
|
||||
@ -4,23 +4,23 @@ import { observer } from '@ali/lowcode-editor-core';
|
||||
import Area from '../area';
|
||||
|
||||
@observer
|
||||
export default class TopArea extends Component<{ area: Area }> {
|
||||
export default class TopArea extends Component<{ area: Area, itemClassName?: string }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
const { area, itemClassName } = this.props;
|
||||
return (
|
||||
<div className={classNames("lc-top-area", {
|
||||
<div className={classNames("lc-top-area engine-actionpane", {
|
||||
'lc-area-visible': area.visible
|
||||
})}>
|
||||
<Contents area={area} />
|
||||
<Contents area={area} itemClassName={itemClassName} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area }> {
|
||||
class Contents extends Component<{ area: Area, itemClassName?: string }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
const { area, itemClassName } = this.props;
|
||||
const left: any[] = [];
|
||||
const center: any[] = [];
|
||||
const right: any[] = [];
|
||||
@ -29,12 +29,17 @@ class Contents extends Component<{ area: Area }> {
|
||||
const index2 = b.config?.index || 0;
|
||||
return index1 === index2 ? 0 : (index1 > index2 ? 1 : -1);
|
||||
}).forEach(item => {
|
||||
const content = (
|
||||
<div className={itemClassName || ''}>
|
||||
{item.content}
|
||||
</div>
|
||||
);
|
||||
if (item.align === 'center') {
|
||||
center.push(item.content);
|
||||
center.push(content);
|
||||
} else if (item.align === 'left') {
|
||||
left.push(item.content);
|
||||
left.push(content);
|
||||
} else {
|
||||
right.push(item.content);
|
||||
right.push(content);
|
||||
}
|
||||
});
|
||||
return (
|
||||
|
||||
@ -54,14 +54,11 @@ body {
|
||||
display: none;
|
||||
}
|
||||
.lc-panel-title {
|
||||
height: 38px;
|
||||
font-size: 14px;
|
||||
background-color: var(--pane-title-bg-color,rgba(31,56,88,.04));
|
||||
// background-color: var(--pane-title-bg-color,rgba(31,56,88,.04));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 15px;
|
||||
border-bottom: 1px solid var(--color-line-normal,rgba(31,56,88,.1));
|
||||
|
||||
.lc-help-tip {
|
||||
margin-left: 4px;
|
||||
@ -69,10 +66,18 @@ body {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
> .lc-panel-title {
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
padding: 0 15px;
|
||||
// border-bottom: 1px solid var(--color-line-normal,rgba(31,56,88,.1));
|
||||
color: #0F1726;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.lc-panel-body {
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
top: 48px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@ -111,6 +116,9 @@ body {
|
||||
}
|
||||
*/
|
||||
}
|
||||
.lc-outline-pane{
|
||||
border-top: 1px solid var(--color-line-normal,rgba(31,56,88,.1));
|
||||
}
|
||||
}
|
||||
.lc-panel {
|
||||
height: 100%;
|
||||
@ -153,23 +161,88 @@ body {
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
/*覆盖旧面板*/
|
||||
/*组件面板*/
|
||||
// .ve-component-list {
|
||||
// .ve-component-list-body{
|
||||
// .ve-component-list-sidebar{
|
||||
// .ve-component-list-navigator{
|
||||
// .navigator-group{
|
||||
// &:last-child{
|
||||
// &::after{
|
||||
// display: none;
|
||||
// }
|
||||
// }
|
||||
// &::after{
|
||||
// content: '';
|
||||
// display: block;
|
||||
// height: 1px;
|
||||
// background-color: #EDEFF3;
|
||||
// line-height: 0;
|
||||
// margin: 4px 12px 0;
|
||||
// }
|
||||
// .navigator-group-head{
|
||||
// .navigator-group-title{
|
||||
// border-bottom: none;
|
||||
// }
|
||||
// }
|
||||
// .navigator-group-item{
|
||||
// border-left: 2px solid transparent;
|
||||
// &.active{
|
||||
// border-left-color: #0079f2;
|
||||
// border-right: none;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
/*数据源*/
|
||||
// .engine-datapool{
|
||||
// .engine-datapool-view-group{
|
||||
// padding-top: 48px;
|
||||
// .engine-datapool-view-group-title{
|
||||
// height: 48px;
|
||||
// line-height: 48px;
|
||||
// font-size: 16px;
|
||||
// background-color: transparent;
|
||||
// padding: 0 16px;
|
||||
// border-bottom: 1px solid #EDEFF3;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
/*动作面板*/
|
||||
// .ve-action-pane{
|
||||
// border-top: none;
|
||||
// .rc-tabs{
|
||||
// .rc-tabs-bar{
|
||||
// background-color: transparent;
|
||||
// .rc-tabs-tab{
|
||||
// line-height: 1;
|
||||
// &.rc-tabs-tab-active{
|
||||
|
||||
.my-dock {
|
||||
padding: 0px 10px;
|
||||
cursor: pointer;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.my-title-label {
|
||||
user-select: none;
|
||||
}
|
||||
&.actived, &:hover {
|
||||
background-color: var(--pane-title-bg-color);
|
||||
.my-title {
|
||||
color: var(--color-actived);
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/*设置面板*/
|
||||
// .ve-field .ve-field-head,
|
||||
// .ve-field.ve-accordion2-field > .ve-field-head .ve-field-title-content{
|
||||
// padding: 0;
|
||||
// }
|
||||
// .ve-field.ve-accordion2-field > .ve-field-split-line{
|
||||
// display: none;
|
||||
// }
|
||||
// .vs-style .vs-style-source{
|
||||
// margin: 0 0 16px;
|
||||
// }
|
||||
// .vs-code-button,
|
||||
// .vs-json-button{
|
||||
// margin: 0;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@ -184,13 +257,13 @@ body {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-bottom: 2px;
|
||||
padding: 8px;
|
||||
.lc-top-area-left{}
|
||||
padding: 8px 12px 8px 16px;
|
||||
|
||||
.lc-top-area-center{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-right: 8px;
|
||||
justify-content: center;
|
||||
margin: 0 8px;
|
||||
}
|
||||
.lc-top-area-right{
|
||||
display: flex;
|
||||
@ -222,8 +295,9 @@ body {
|
||||
}
|
||||
.lc-pane-close{
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 6px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
.next-icon{
|
||||
line-height: 1;
|
||||
@ -231,17 +305,32 @@ body {
|
||||
}
|
||||
.lc-tabs-title {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
display: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(31,56,88,0.04);
|
||||
// background: rgba(31,56,88,0.04);
|
||||
border-bottom: 1px solid #EDEFF3;
|
||||
.lc-tab-title{
|
||||
flex: 1;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
&.actived {
|
||||
color: #0079F2;
|
||||
border-bottom-color: #0079F2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.lc-tabs-content {
|
||||
position: absolute;
|
||||
top: 36px;
|
||||
top: 32px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@ -265,16 +354,23 @@ body {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
.lc-title{
|
||||
padding: 12px;
|
||||
.lc-title {
|
||||
flex-direction: column;
|
||||
&.has-tip{
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.has-tip {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.actived{
|
||||
color: #0079F2;
|
||||
}
|
||||
.lc-title-icon{
|
||||
.lc-title-icon {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin: 0;
|
||||
.next-icon:before {
|
||||
line-height: 1 !important;
|
||||
@ -301,8 +397,9 @@ body {
|
||||
}
|
||||
.lc-pane-close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 6px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
.next-icon {
|
||||
line-height: 1;
|
||||
@ -324,6 +421,7 @@ body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 10;
|
||||
.lc-toolbar {
|
||||
height: var(--toolbar-height);
|
||||
background-color: var(--color-pane-background);
|
||||
|
||||
@ -13,16 +13,16 @@ import RightArea from './right-area';
|
||||
import './workbench.less';
|
||||
|
||||
@observer
|
||||
export class Workbench extends Component<{ skeleton: Skeleton, className?: string }> {
|
||||
export class Workbench extends Component<{ skeleton: Skeleton, className?: string, topAreaItemClassName?: string }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { skeleton, className } = this.props;
|
||||
const { skeleton, className, topAreaItemClassName } = this.props;
|
||||
return (
|
||||
<div className={classNames('lc-workbench', className)}>
|
||||
<TopArea area={skeleton.topArea} />
|
||||
<TopArea area={skeleton.topArea} itemClassName={topAreaItemClassName} />
|
||||
<div className="lc-workbench-body">
|
||||
<LeftArea area={skeleton.leftArea} />
|
||||
<LeftFloatPane area={skeleton.leftFloatArea} />
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { TransformedComponentMetadata, FieldConfig, SettingTarget } from '@ali/lowcode-types';
|
||||
import { IconSlot } from '../icons/slot';
|
||||
|
||||
export default function(metadata: TransformedComponentMetadata): TransformedComponentMetadata {
|
||||
const { componentName, configure = {} } = metadata;
|
||||
@ -46,7 +47,7 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
const supportedLifecycles =
|
||||
events.supportedLifecycles ||
|
||||
(isRoot
|
||||
? [
|
||||
? /*[
|
||||
{
|
||||
description: '初始化时',
|
||||
name: 'constructor',
|
||||
@ -63,7 +64,7 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
description: '卸载时',
|
||||
name: 'componentWillUnmount',
|
||||
},
|
||||
]
|
||||
]*/ null
|
||||
: null);
|
||||
if (supportedLifecycles) {
|
||||
eventsDefinition.push({
|
||||
@ -80,7 +81,22 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
});
|
||||
}
|
||||
// 通用设置
|
||||
const propsGroup = props || [];
|
||||
let propsGroup = props || [];
|
||||
const basicInfo: any = {};
|
||||
if (componentName === 'Slot') {
|
||||
basicInfo.icon = IconSlot;
|
||||
propsGroup = [{
|
||||
name: '___title',
|
||||
title: {
|
||||
type: 'i18n',
|
||||
'en-US': 'Slot Title',
|
||||
'zh-CN': '插槽标题'
|
||||
},
|
||||
setter: 'StringSetter',
|
||||
defaultValue: '插槽容器'
|
||||
}]
|
||||
}
|
||||
/*
|
||||
propsGroup.push({
|
||||
name: '#generals',
|
||||
title: { type: 'i18n', 'zh-CN': '通用', 'en-US': 'General' },
|
||||
@ -101,14 +117,14 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
title: 'Ref',
|
||||
setter: 'StringSetter',
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: '!more',
|
||||
title: '更多',
|
||||
setter: 'PropertiesSetter',
|
||||
},*/
|
||||
},
|
||||
],
|
||||
});
|
||||
*/
|
||||
const combined: FieldConfig[] = [
|
||||
{
|
||||
title: { type: 'i18n', 'zh-CN': '属性', 'en-US': 'Props' },
|
||||
@ -182,8 +198,15 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
items: [
|
||||
{
|
||||
name: '___condition',
|
||||
title: { type: 'i18n', 'zh-CN': '条件显示', 'en-US': 'Condition' },
|
||||
setter: 'ExpressionSetter',
|
||||
title: { type: 'i18n', 'zh-CN': '是否渲染', 'en-US': 'Condition' },
|
||||
setter: [{
|
||||
componentName: 'BoolSetter',
|
||||
props: {
|
||||
defaultValue: true,
|
||||
}
|
||||
}, {
|
||||
componentName: 'VariableSetter'
|
||||
}],
|
||||
},
|
||||
{
|
||||
name: '#loop',
|
||||
@ -192,27 +215,14 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
{
|
||||
name: '___loop',
|
||||
title: { type: 'i18n', 'zh-CN': '循环数据', 'en-US': 'Loop Data' },
|
||||
setter: {
|
||||
componentName: 'MixinSetter',
|
||||
setter: [{
|
||||
componentName: 'JsonSetter',
|
||||
props: {
|
||||
// TODO:
|
||||
setters: [
|
||||
{
|
||||
componentName: 'JSONSetter',
|
||||
props: {
|
||||
mode: 'popup',
|
||||
placeholder: { type: 'i18n', 'zh-CN': '编辑数据', 'en-US': 'Edit Data' },
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'ExpressionSetter',
|
||||
props: {
|
||||
placeholder: { type: 'i18n', 'zh-CN': '绑定数据', 'en-US': 'Bind Data' },
|
||||
},
|
||||
},
|
||||
],
|
||||
label: { type: 'i18n', 'zh-CN': '编辑数据', 'en-US': 'Edit Data'},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
componentName: 'VariableSetter'
|
||||
}],
|
||||
},
|
||||
{
|
||||
name: '___loopArgs.0',
|
||||
@ -236,8 +246,12 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
},
|
||||
{
|
||||
name: 'key',
|
||||
title: 'Key',
|
||||
setter: 'ExpressionSetter',
|
||||
title: '循环 Key',
|
||||
setter: [{
|
||||
componentName: 'StringSetter',
|
||||
}, {
|
||||
componentName: 'VariableSetter'
|
||||
}],
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -247,6 +261,7 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
...basicInfo,
|
||||
configure: {
|
||||
...configure,
|
||||
combined,
|
||||
|
||||
@ -45,6 +45,8 @@ export default class Dock implements IWidget {
|
||||
} else {
|
||||
this._body = createElement(DockView, props);
|
||||
}
|
||||
this.inited = true;
|
||||
|
||||
return this._body;
|
||||
}
|
||||
|
||||
|
||||
@ -59,14 +59,14 @@ export default class PanelDock implements IWidget {
|
||||
this.id = uniqueId(`dock:${name}$`);
|
||||
this.panelName = config.panelName || name;
|
||||
if (content) {
|
||||
const _panelProps: any = { ...panelProps };
|
||||
if (_panelProps.title == null && props) {
|
||||
_panelProps.title = composeTitle(props.title, undefined, props.description, true, true);
|
||||
}
|
||||
this._panel = this.skeleton.add({
|
||||
type: "Panel",
|
||||
name: this.panelName,
|
||||
props: {
|
||||
// FIXME! give default title for panel
|
||||
title: props ? composeTitle(props?.title, props?.icon, props?.description, true) : '',
|
||||
...panelProps,
|
||||
},
|
||||
props: _panelProps,
|
||||
contentProps,
|
||||
content,
|
||||
area: panelProps?.area
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { createElement, ReactNode } from 'react';
|
||||
import { obx } from '@ali/lowcode-editor-core';
|
||||
import { uniqueId, createContent } from '@ali/lowcode-utils';
|
||||
@ -13,8 +14,9 @@ export default class Panel implements IWidget {
|
||||
readonly isWidget = true;
|
||||
readonly name: string;
|
||||
readonly id: string;
|
||||
@obx.ref inited: boolean = false;
|
||||
@obx.ref private _actived: boolean = false;
|
||||
@obx.ref inited = false;
|
||||
@obx.ref private _actived = false;
|
||||
private emitter = new EventEmitter();
|
||||
get actived(): boolean {
|
||||
return this._actived;
|
||||
}
|
||||
@ -46,7 +48,7 @@ export default class Panel implements IWidget {
|
||||
|
||||
readonly title: TitleContent;
|
||||
readonly help?: HelpTipConfig;
|
||||
private plain: boolean = false;
|
||||
private plain = false;
|
||||
|
||||
private container?: WidgetContainer<Panel, PanelConfig>;
|
||||
private parent?: WidgetContainer;
|
||||
@ -60,6 +62,9 @@ export default class Panel implements IWidget {
|
||||
this.plain = hideTitleBar || !title;
|
||||
this.help = help;
|
||||
if (Array.isArray(content)) {
|
||||
if (content.length === 1) {
|
||||
// todo: not show tabs
|
||||
}
|
||||
this.container = this.skeleton.createContainer(
|
||||
name,
|
||||
(item) => {
|
||||
@ -121,7 +126,11 @@ export default class Panel implements IWidget {
|
||||
}
|
||||
|
||||
active(item?: Panel | string | null) {
|
||||
this.container?.active(item);
|
||||
if (item) {
|
||||
this.container?.active(item);
|
||||
} else {
|
||||
this.setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
getName() {
|
||||
@ -143,9 +152,11 @@ export default class Panel implements IWidget {
|
||||
}
|
||||
this._actived = true;
|
||||
this.parent?.active(this);
|
||||
this.emitter.emit('activechange', true);
|
||||
} else if (this.inited) {
|
||||
this._actived = false;
|
||||
this.parent?.unactive(this);
|
||||
this.emitter.emit('activechange', false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,6 +190,16 @@ export default class Panel implements IWidget {
|
||||
* @deprecated
|
||||
*/
|
||||
setPosition(position: string) {
|
||||
// noop
|
||||
}
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
onActiveChange(fn: (flag: boolean) => void): () => void {
|
||||
this.emitter.on('activechange', fn);
|
||||
return () => {
|
||||
this.emitter.removeListener('activechange', fn);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { IconType, TitleContent, isI18nData, TipContent } from '@ali/lowcode-types';
|
||||
import { IconType, TitleContent, isI18nData, TipContent, isTitleConfig } from '@ali/lowcode-types';
|
||||
import { isValidElement } from 'react';
|
||||
|
||||
export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipContent, tipAsTitle?: boolean) {
|
||||
export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipContent, tipAsTitle?: boolean, noIcon?: boolean) {
|
||||
if (!title) {
|
||||
title = {};
|
||||
if (!icon || tipAsTitle) {
|
||||
@ -11,6 +11,19 @@ export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipCon
|
||||
}
|
||||
if (icon || tip) {
|
||||
if (typeof title !== 'object' || isValidElement(title) || isI18nData(title)) {
|
||||
if (isValidElement(title)) {
|
||||
if (title.type === 'svg' || (title.type as any).getIcon) {
|
||||
if (!icon) {
|
||||
icon = title as any;
|
||||
}
|
||||
if (tipAsTitle) {
|
||||
title = tip as any;
|
||||
tip = null;
|
||||
} else {
|
||||
title = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
title = {
|
||||
label: title,
|
||||
icon,
|
||||
@ -24,5 +37,8 @@ export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipCon
|
||||
};
|
||||
}
|
||||
}
|
||||
if (isTitleConfig(title) && noIcon) {
|
||||
title.icon = undefined;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ export default class WidgetContainer<T extends WidgetItem = any, G extends Widge
|
||||
if (nameOrItem && typeof nameOrItem === 'string') {
|
||||
item = this.get(nameOrItem);
|
||||
}
|
||||
if (!isActiveable(nameOrItem)) {
|
||||
if (!isActiveable(item)) {
|
||||
item = null;
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ export default class WidgetContainer<T extends WidgetItem = any, G extends Widge
|
||||
if (nameOrItem && typeof nameOrItem === 'string') {
|
||||
item = this.get(nameOrItem);
|
||||
}
|
||||
if (!isActiveable(nameOrItem)) {
|
||||
if (!isActiveable(item)) {
|
||||
item = null;
|
||||
}
|
||||
if (this._current === item) {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import Pane from './views/pane';
|
||||
import { IconOutline } from './icons/outline';
|
||||
import { intl } from './locale';
|
||||
import { intlNode } from './locale';
|
||||
|
||||
export default {
|
||||
name: 'outline-pane',
|
||||
props: {
|
||||
icon: IconOutline,
|
||||
description: intl('Outline Tree'),
|
||||
description: intlNode('Outline Tree'),
|
||||
},
|
||||
content: Pane,
|
||||
};
|
||||
|
||||
@ -2,9 +2,9 @@ import { createIntl } from '@ali/lowcode-editor-core';
|
||||
import en_US from './en-US.json';
|
||||
import zh_CN from './zh-CN.json';
|
||||
|
||||
const { intl, getLocale, setLocale } = createIntl({
|
||||
const { intl, intlNode, getLocale, setLocale } = createIntl({
|
||||
'en-US': en_US,
|
||||
'zh-CN': zh_CN,
|
||||
});
|
||||
|
||||
export { intl, getLocale, setLocale };
|
||||
export { intl, intlNode, getLocale, setLocale };
|
||||
|
||||
@ -122,7 +122,7 @@ export default class TreeNode {
|
||||
return title;
|
||||
}
|
||||
if (isI18nData(title)) {
|
||||
return intl(title);
|
||||
return intl(title) as string;
|
||||
}
|
||||
return this.node.componentName;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { observer, Title } from '@ali/lowcode-editor-core';
|
||||
import { ExclusiveGroup } from '@ali/lowcode-designer';
|
||||
import TreeNode from '../tree-node';
|
||||
import TreeNodeView from './tree-node';
|
||||
import { intl } from '../locale';
|
||||
import { intlNode } from '../locale';
|
||||
|
||||
@observer
|
||||
export default class TreeBranches extends Component<{
|
||||
@ -119,7 +119,7 @@ class TreeNodeSlots extends Component<{
|
||||
data-id={treeNode.id}
|
||||
>
|
||||
<div className="tree-node-slots-title">
|
||||
<Title title={{ type: 'i18n', intl: intl('Slots') }} />
|
||||
<Title title={{ type: 'i18n', intl: intlNode('Slots') }} />
|
||||
</div>
|
||||
{treeNode.slots.map(tnode => (
|
||||
<TreeNodeView key={tnode.id} treeNode={tnode} />
|
||||
|
||||
@ -5,12 +5,11 @@ import { IconArrowRight } from '../icons/arrow-right';
|
||||
import { IconEyeClose } from '../icons/eye-close';
|
||||
import { IconLock } from '../icons/lock';
|
||||
import { IconUnlock } from '../icons/unlock';
|
||||
import { intl } from '../locale';
|
||||
import { intl, intlNode } from '../locale';
|
||||
import TreeNode from '../tree-node';
|
||||
import { IconEye } from '../icons/eye';
|
||||
import { IconCond } from '../icons/cond';
|
||||
import { IconLoop } from '../icons/loop';
|
||||
import { IconSlot } from '../icons/slot';
|
||||
import { createIcon } from '@ali/lowcode-utils';
|
||||
|
||||
@observer
|
||||
@ -104,29 +103,28 @@ export default class TreeTitle extends Component<{
|
||||
{node.slotFor && (
|
||||
<a className="tree-node-tag slot">
|
||||
{/* todo: click redirect to prop */}
|
||||
<IconSlot />
|
||||
<Tip>{intl('Slot for {prop}', { prop: node.slotFor.key })}</Tip>
|
||||
<Tip>{intlNode('Slot for {prop}', { prop: node.slotFor.key })}</Tip>
|
||||
</a>
|
||||
)}
|
||||
{node.hasLoop() && (
|
||||
<a className="tree-node-tag loop">
|
||||
{/* todo: click todo something */}
|
||||
<IconLoop />
|
||||
<Tip>{intl('Loop')}</Tip>
|
||||
<Tip>{intlNode('Loop')}</Tip>
|
||||
</a>
|
||||
)}
|
||||
{node.hasCondition() && !node.conditionGroup && (
|
||||
<a className="tree-node-tag cond">
|
||||
{/* todo: click todo something */}
|
||||
<IconCond />
|
||||
<Tip>{intl('Conditional')}</Tip>
|
||||
<Tip>{intlNode('Conditional')}</Tip>
|
||||
</a>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
{isCNode && isNodeParent && <HideBtn treeNode={treeNode} />}
|
||||
{isCNode && isNodeParent && <LockBtn treeNode={treeNode} />}
|
||||
{/*isCNode && isNodeParent && <LockBtn treeNode={treeNode} />*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,6 +4,6 @@
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": [
|
||||
"./src/"
|
||||
"./src/",
|
||||
]
|
||||
}
|
||||
|
||||
@ -228,6 +228,10 @@ export default class BaseEngine extends PureComponent {
|
||||
|
||||
let Comp = components[schema.componentName] || Div;
|
||||
|
||||
if (schema.hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (schema.loop !== undefined) {
|
||||
return this.__createLoopVirtualDom(
|
||||
{
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"@recore/obx-react": "^1.0.7",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16",
|
||||
"@ali/vu-css-style": "^1.0.2",
|
||||
"react-dom": "^16.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -6,7 +6,7 @@ class Slot extends Component {
|
||||
componentName: 'Slot',
|
||||
configure: {
|
||||
props: [{
|
||||
name: '___title___',
|
||||
name: '___title',
|
||||
title: {
|
||||
type: 'i18n',
|
||||
'en-US': 'Slot Title',
|
||||
@ -15,7 +15,7 @@ class Slot extends Component {
|
||||
setter: 'StringSetter',
|
||||
defaultValue: '插槽容器'
|
||||
}, {
|
||||
name: '___params___',
|
||||
name: '___params',
|
||||
title: {
|
||||
type: 'i18n',
|
||||
'en-US': 'Slot Params',
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import LowCodeRenderer from '@ali/lowcode-react-renderer';
|
||||
import { isObject } from 'lodash';
|
||||
import { ReactInstance, Fragment, Component, createElement } from 'react';
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { SimulatorRenderer } from './renderer';
|
||||
@ -7,7 +8,7 @@ import './renderer.less';
|
||||
|
||||
// patch cloneElement avoid lost keyProps
|
||||
const originCloneElement = window.React.cloneElement;
|
||||
(window as any).React.cloneElement = (child: any, { _leaf, ...props}: any = {}) => {
|
||||
(window as any).React.cloneElement = (child: any, { _leaf, ...props }: any = {}) => {
|
||||
if (child.ref && props.ref) {
|
||||
const dRef = props.ref;
|
||||
const cRef = child.ref;
|
||||
@ -18,7 +19,7 @@ const originCloneElement = window.React.cloneElement;
|
||||
} else {
|
||||
try {
|
||||
cRef.current = x;
|
||||
} catch (e) { }
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
if (dRef) {
|
||||
@ -27,13 +28,13 @@ const originCloneElement = window.React.cloneElement;
|
||||
} else {
|
||||
try {
|
||||
dRef.current = x;
|
||||
} catch (e) { }
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
return originCloneElement(child, props);
|
||||
}
|
||||
};
|
||||
|
||||
export default class SimulatorRendererView extends Component<{ renderer: SimulatorRenderer }> {
|
||||
render() {
|
||||
@ -84,10 +85,11 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
|
||||
const { __id, __desingMode, ...viewProps } = props;
|
||||
viewProps.componentId = __id;
|
||||
viewProps._leaf = host.document.getNode(__id);
|
||||
|
||||
return createElement(
|
||||
Component,
|
||||
viewProps,
|
||||
children == null ? null : Array.isArray(children) ? children : [children],
|
||||
children == null ? [] : Array.isArray(children) ? children : [children],
|
||||
);
|
||||
}}
|
||||
onCompGetRef={(schema: any, ref: ReactInstance | null) => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ReactElement, ReactNode } from 'react';
|
||||
import { I18nData } from './i18n';
|
||||
import { I18nData, isI18nData } from './i18n';
|
||||
import { TipContent } from './tip';
|
||||
import { IconType } from './icon';
|
||||
|
||||
@ -13,3 +13,14 @@ export interface TitleConfig {
|
||||
|
||||
export type TitleContent = string | I18nData | ReactElement | TitleConfig;
|
||||
|
||||
function isPlainObject(value: any): value is object {
|
||||
if (typeof value !== 'object') {
|
||||
return false;
|
||||
}
|
||||
const proto = Object.getPrototypeOf(value);
|
||||
return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null;
|
||||
}
|
||||
|
||||
export function isTitleConfig(obj: any): obj is TitleConfig {
|
||||
return isPlainObject(obj) && !isI18nData(obj);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { isObject } from './is-object';
|
||||
|
||||
export function isPlainObject(value: any) {
|
||||
export function isPlainObject(value: any): value is object {
|
||||
if (!isObject(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -100,6 +100,22 @@ export default class Bundle {
|
||||
cp.setView(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO dirty fix
|
||||
*/
|
||||
addComponentBundle(bundles: any) {
|
||||
/**
|
||||
* Normal Component bundle: [ Prototype, PrototypeView ]
|
||||
* Component without Prototype.js: [ View ]
|
||||
*/
|
||||
if (bundles.length >= 2) {
|
||||
const prototype = bundles[0];
|
||||
const prototypeView = bundles[1];
|
||||
prototype.setView(prototypeView);
|
||||
this.registerPrototype(prototype);
|
||||
}
|
||||
}
|
||||
|
||||
private recursivelyRegisterViews(list: any[], viewName?: string): void {
|
||||
list.forEach((item: any) => {
|
||||
if (Array.isArray(item.module)) {
|
||||
|
||||
@ -237,7 +237,7 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
}
|
||||
}
|
||||
|
||||
if (collapse || collapsed || fieldCollapsed) {
|
||||
if (collapse || collapsed || fieldCollapsed || extraProps.display === DISPLAY_TYPE.ENTRY) {
|
||||
extraProps.defaultCollapsed = true;
|
||||
}
|
||||
function isDisabled(field: Field) {
|
||||
@ -287,10 +287,14 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
if (slotName && initialValue === true) {
|
||||
initialFn = (field: any, value: any) => {
|
||||
if (isJSSlot(value)) {
|
||||
return value;
|
||||
return {
|
||||
title: slotTitle || title,
|
||||
...value,
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: 'JSSlot',
|
||||
title: slotTitle || title,
|
||||
value: initialChildren,
|
||||
};
|
||||
};
|
||||
@ -351,10 +355,14 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
componentName: 'SlotSetter',
|
||||
initialValue: (field: any, value: any) => {
|
||||
if (isJSSlot(value)) {
|
||||
return value;
|
||||
return {
|
||||
title: slotTitle || title,
|
||||
...value,
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: 'JSSlot',
|
||||
title: slotTitle || title,
|
||||
value: value == null ? initialChildren : value,
|
||||
};
|
||||
},
|
||||
|
||||
82
packages/vision-preset/src/components/index.less
Normal file
82
packages/vision-preset/src/components/index.less
Normal file
@ -0,0 +1,82 @@
|
||||
@import '~@ali/ve-less-variables/index.less';
|
||||
|
||||
// 样式直接沿用之前的样式,优化了下命名
|
||||
.instance-node-selector {
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
color: var(--color-icon-white, @title-bgcolor);
|
||||
border-radius: @global-border-radius;
|
||||
margin-right: 2px;
|
||||
pointer-events: auto;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 5px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
max-width: inherit;
|
||||
path {
|
||||
fill: var(--color-icon-white, @title-bgcolor);
|
||||
}
|
||||
}
|
||||
&-current {
|
||||
background: var(--color-brand, @brand-color-1);
|
||||
padding: 0 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
color: var(--color-icon-white, @title-bgcolor);
|
||||
border-radius: 3px;
|
||||
|
||||
&-title {
|
||||
padding-right: 6px;
|
||||
color: var(--color-icon-white, @title-bgcolor);
|
||||
}
|
||||
}
|
||||
&-list {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
&-node {
|
||||
margin: 2px 0;
|
||||
&-content {
|
||||
padding-left: 6px;
|
||||
background: #78869a;
|
||||
display: inline-flex;
|
||||
border-radius: 3px;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
color: var(--color-icon-white, @title-bgcolor);
|
||||
cursor: pointer;
|
||||
overflow: visible;
|
||||
}
|
||||
&-title {
|
||||
padding-right: 6px;
|
||||
// margin-left: 5px;
|
||||
color: var(--color-icon-white, @title-bgcolor);
|
||||
cursor: pointer;
|
||||
overflow: visible;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.instance-node-selector-current {
|
||||
color: ar(--color-text-reverse, @white-alpha-2);
|
||||
}
|
||||
.instance-node-selector-popup {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: 0.2s all ease-in;
|
||||
}
|
||||
}
|
||||
110
packages/vision-preset/src/components/index.tsx
Normal file
110
packages/vision-preset/src/components/index.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import { Overlay } from '@alifd/next';
|
||||
import React from 'react';
|
||||
import './index.less';
|
||||
import { Title } from '@ali/lowcode-editor-core';
|
||||
|
||||
import { Node, ParentalNode } from '@ali/lowcode-designer';
|
||||
|
||||
const { Popup } = Overlay;
|
||||
|
||||
export interface IProps {
|
||||
node: Node;
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
parentNodes: Node[];
|
||||
}
|
||||
|
||||
type UnionNode = Node | ParentalNode | null;
|
||||
|
||||
export class InstanceNodeSelector extends React.Component<IProps, IState> {
|
||||
state: IState = {
|
||||
parentNodes: [],
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const parentNodes = this.getParentNodes(this.props.node);
|
||||
this.setState({
|
||||
parentNodes,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取节点的父级节点(最多获取5层)
|
||||
getParentNodes = (node: Node) => {
|
||||
const parentNodes = [];
|
||||
let currentNode: UnionNode = node;
|
||||
|
||||
while (currentNode && parentNodes.length < 5) {
|
||||
currentNode = currentNode.getParent();
|
||||
if (currentNode) {
|
||||
parentNodes.push(currentNode);
|
||||
}
|
||||
}
|
||||
return parentNodes;
|
||||
};
|
||||
|
||||
onSelect = (node: Node) => () => {
|
||||
if (node && typeof node.select === 'function') {
|
||||
node.select();
|
||||
}
|
||||
};
|
||||
onMouseOver = (node: Node) => (_: any, flag = true) => {
|
||||
if (node && typeof node.hover === 'function') {
|
||||
node.hover(flag);
|
||||
}
|
||||
};
|
||||
onMouseOut = (node: Node) => (_: any, flag = false) => {
|
||||
if (node && typeof node.hover === 'function') {
|
||||
node.hover(flag);
|
||||
}
|
||||
};
|
||||
renderNodes = (node: Node) => {
|
||||
const nodes = this.state.parentNodes || [];
|
||||
const children = nodes.map((node, key) => {
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
onClick={this.onSelect(node)}
|
||||
onMouseEnter={this.onMouseOver(node)}
|
||||
onMouseLeave={this.onMouseOut(node)}
|
||||
className="instance-node-selector-node"
|
||||
>
|
||||
<div className="instance-node-selector-node-content">
|
||||
<Title
|
||||
className="instance-node-selector-node-title"
|
||||
title={{
|
||||
label: node.title,
|
||||
icon: node.icon,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return children;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { node } = this.props;
|
||||
return (
|
||||
<div className="instance-node-selector">
|
||||
<Popup
|
||||
trigger={
|
||||
<div className="instance-node-selector-current">
|
||||
<Title
|
||||
className="instance-node-selector-node-title"
|
||||
title={{
|
||||
label: node.title,
|
||||
icon: node.icon,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
triggerType="hover"
|
||||
>
|
||||
<div className="instance-node-selector">{this.renderNodes(node)}</div>
|
||||
</Popup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,16 @@
|
||||
import { isJSBlock, isJSSlot } from '@ali/lowcode-types';
|
||||
import { isPlainObject } from '@ali/lowcode-utils';
|
||||
import { globalContext, Editor, registerSetter } from '@ali/lowcode-editor-core';
|
||||
import { Designer, TransformStage } from '@ali/lowcode-designer';
|
||||
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||
import { Designer, TransformStage, addBuiltinComponentAction } from '@ali/lowcode-designer';
|
||||
// import { registerSetters } from '@ali/lowcode-setters';
|
||||
import Outline from '@ali/lowcode-plugin-outline-pane';
|
||||
import { toCss } from '@ali/vu-css-style';
|
||||
|
||||
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
||||
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
|
||||
|
||||
import Preview from '@ali/lowcode-plugin-sample-preview';
|
||||
// import SourceEditor from '@ali/lowcode-plugin-source-editor';
|
||||
import { i18nReducer } from './i18n-reducer';
|
||||
import { InstanceNodeSelector } from './components';
|
||||
|
||||
export const editor = new Editor();
|
||||
globalContext.register(editor, Editor);
|
||||
@ -66,6 +67,37 @@ function upgradePropsReducer(props: any) {
|
||||
}
|
||||
designer.addPropsReducer(upgradePropsReducer, TransformStage.Init);
|
||||
|
||||
// 设计器组件样式处理
|
||||
function stylePropsReducer(props: any, node: any) {
|
||||
if (props && typeof props === 'object' && props.__style__) {
|
||||
const doc = designer.currentDocument?.simulator?.contentDocument;
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
const cssId = '_style_pesudo_' + node.id.replace(/\$/g, '_');
|
||||
const cssClass = '_css_pesudo_' + node.id.replace(/\$/g, '_');
|
||||
const dom = doc.getElementById(cssId);
|
||||
if (dom) {
|
||||
dom.parentNode?.removeChild(dom);
|
||||
}
|
||||
let styleProp = props.__style__;
|
||||
if (typeof styleProp === 'object') {
|
||||
styleProp = toCss(styleProp);
|
||||
}
|
||||
if (typeof styleProp === 'string') {
|
||||
const s = doc.createElement('style');
|
||||
props.className = cssClass;
|
||||
s.setAttribute('type', 'text/css');
|
||||
s.setAttribute('id', cssId);
|
||||
doc.getElementsByTagName('head')[0].appendChild(s);
|
||||
|
||||
s.appendChild(doc.createTextNode(styleProp.replace(/:root/g, '.' + cssClass)));
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
designer.addPropsReducer(stylePropsReducer, TransformStage.Render);
|
||||
|
||||
skeleton.add({
|
||||
area: 'mainArea',
|
||||
name: 'designer',
|
||||
@ -88,16 +120,6 @@ skeleton.add({
|
||||
},
|
||||
});
|
||||
|
||||
skeleton.add({
|
||||
area: 'topArea',
|
||||
type: 'Dock',
|
||||
name: 'preview',
|
||||
props: {
|
||||
align: 'right',
|
||||
},
|
||||
content: Preview,
|
||||
});
|
||||
|
||||
// skeleton.add({
|
||||
// name: 'sourceEditor',
|
||||
// type: 'PanelDock',
|
||||
@ -112,3 +134,11 @@ skeleton.add({
|
||||
// },
|
||||
// content: SourceEditor,
|
||||
// });
|
||||
|
||||
// 实例节点选择器,线框高亮
|
||||
addBuiltinComponentAction({
|
||||
name: 'instance-node-selector',
|
||||
content: InstanceNodeSelector,
|
||||
important: true,
|
||||
condition: 'always'
|
||||
});
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { ALI_SCHEMA_VERSION } from './base/const';
|
||||
import { obx } from '@ali/lowcode-editor-core';
|
||||
|
||||
interface ILiteralObject {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export class Env {
|
||||
|
||||
public envs: ILiteralObject;
|
||||
@obx.val envs: ILiteralObject = {};
|
||||
|
||||
private emitter: EventEmitter;
|
||||
private featureMap: ILiteralObject;
|
||||
@ -15,23 +15,22 @@ export class Env {
|
||||
constructor() {
|
||||
this.emitter = new EventEmitter();
|
||||
this.emitter.setMaxListeners(0);
|
||||
this.envs = {};
|
||||
this.featureMap = {};
|
||||
}
|
||||
|
||||
public get(name: string): any {
|
||||
get(name: string): any {
|
||||
return this.getEnv(name);
|
||||
}
|
||||
|
||||
public getEnv(name: string): any {
|
||||
getEnv(name: string): any {
|
||||
return this.envs[name];
|
||||
}
|
||||
|
||||
public set(name: string, value: any) {
|
||||
set(name: string, value: any) {
|
||||
return this.setEnv(name, value);
|
||||
}
|
||||
|
||||
public setEnv(name: string, value: any) {
|
||||
setEnv(name: string, value: any) {
|
||||
const orig = this.envs[name];
|
||||
if (JSON.stringify(orig) === JSON.stringify(value)) {
|
||||
return;
|
||||
@ -40,47 +39,47 @@ export class Env {
|
||||
this.emitter.emit('envchange', this.envs, name, value);
|
||||
}
|
||||
|
||||
public setEnvMap(envs: ILiteralObject): void {
|
||||
setEnvMap(envs: ILiteralObject): void {
|
||||
this.envs = Object.assign(this.envs, envs);
|
||||
this.emitter.emit('envchange', this.envs);
|
||||
}
|
||||
|
||||
public getLocale(): string {
|
||||
getLocale(): string {
|
||||
return this.getEnv('locale') || 'zh_CN';
|
||||
}
|
||||
|
||||
public setLocale(locale: string) {
|
||||
setLocale(locale: string) {
|
||||
this.setEnv('locale', locale);
|
||||
}
|
||||
|
||||
public setExpertMode(flag: string) {
|
||||
setExpertMode(flag: string) {
|
||||
this.setEnv('expertMode', !!flag);
|
||||
}
|
||||
|
||||
public isExpertMode() {
|
||||
isExpertMode() {
|
||||
return !!this.getEnv('expertMode');
|
||||
}
|
||||
|
||||
public getSupportFeatures() {
|
||||
getSupportFeatures() {
|
||||
return Object.assign({}, this.featureMap);
|
||||
}
|
||||
|
||||
public setSupportFeatures(features: ILiteralObject) {
|
||||
setSupportFeatures(features: ILiteralObject) {
|
||||
this.featureMap = Object.assign({}, this.featureMap, features);
|
||||
}
|
||||
|
||||
public supports(name = 'supports') {
|
||||
supports(name = 'supports') {
|
||||
return !!this.featureMap[name];
|
||||
}
|
||||
|
||||
public onEnvChange(func: (envs: ILiteralObject, name: string, value: any) => any) {
|
||||
onEnvChange(func: (envs: ILiteralObject, name: string, value: any) => any) {
|
||||
this.emitter.on('envchange', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('envchange', func);
|
||||
};
|
||||
}
|
||||
|
||||
public getAliSchemaVersion() {
|
||||
getAliSchemaVersion() {
|
||||
return ALI_SCHEMA_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,4 +12,13 @@ export default {
|
||||
const nodes = designer.currentSelection?.getNodes();
|
||||
return nodes?.[0];
|
||||
},
|
||||
/**
|
||||
* TODO dirty fix
|
||||
*/
|
||||
onIntoView(func: (node: any, insertion: any) => any) {
|
||||
// this.emitter.on('intoview', func);
|
||||
return () => {
|
||||
// this.emitter.removeListener('intoview', func);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const I18nUtil = require('@ali/ve-i18n-util');
|
||||
import Env from './env';
|
||||
const I18nUtil = require('@ali/ve-i18n-util');
|
||||
|
||||
interface I18nObject {
|
||||
type?: string;
|
||||
@ -9,7 +9,9 @@ interface I18nObject {
|
||||
}
|
||||
|
||||
export function i18nReducer(obj?: any): any {
|
||||
if (!obj) { return obj; }
|
||||
if (!obj) {
|
||||
return obj;
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => i18nReducer(item));
|
||||
}
|
||||
@ -18,6 +20,7 @@ export function i18nReducer(obj?: any): any {
|
||||
// FIXME! use editor.get
|
||||
let locale = Env.getLocale();
|
||||
if (obj.key) {
|
||||
// FIXME: 此处需要升级I18nUtil,改成响应式
|
||||
return I18nUtil.get(obj.key, locale);
|
||||
}
|
||||
if (locale !== 'zh_CN' && locale !== 'zh_TW' && !obj[locale]) {
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import * as utils from '@ali/ve-utils';
|
||||
import Popup from '@ali/ve-popups';
|
||||
import Icons from '@ali/ve-icons';
|
||||
import logger from '@ali/vu-logger';
|
||||
import { render } from 'react-dom';
|
||||
import I18nUtil from '@ali/ve-i18n-util';
|
||||
import { hotkey as Hotkey } from '@ali/lowcode-editor-core';
|
||||
import { createElement } from 'react';
|
||||
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './base/const';
|
||||
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS, VERSION as Version } from './base/const';
|
||||
import Bus from './bus';
|
||||
import { skeleton } from './editor';
|
||||
import { Workbench } from '@ali/lowcode-editor-skeleton';
|
||||
@ -21,7 +22,10 @@ import * as Field from './fields';
|
||||
import Prop from './prop';
|
||||
import Env from './env';
|
||||
import DragEngine from './drag-engine';
|
||||
import Viewport from './viewport';
|
||||
import Project from './project';
|
||||
import { designer, editor } from './editor';
|
||||
import Symbols from './symbols';
|
||||
|
||||
import './vision.less';
|
||||
|
||||
@ -41,6 +45,7 @@ function init(container?: Element) {
|
||||
createElement(Workbench, {
|
||||
skeleton,
|
||||
className: 'engine-main',
|
||||
topAreaItemClassName: 'engine-actionitem',
|
||||
}),
|
||||
container,
|
||||
);
|
||||
@ -101,6 +106,11 @@ const VisualEngine = {
|
||||
Bundle,
|
||||
Pages,
|
||||
DragEngine,
|
||||
Viewport,
|
||||
Version,
|
||||
Project,
|
||||
logger,
|
||||
Symbols,
|
||||
};
|
||||
|
||||
(window as any).VisualEngine = VisualEngine;
|
||||
@ -144,6 +154,11 @@ export {
|
||||
Bundle,
|
||||
Pages,
|
||||
DragEngine,
|
||||
Viewport,
|
||||
Version,
|
||||
Project,
|
||||
logger,
|
||||
Symbols,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -6,17 +6,28 @@ const { project } = designer;
|
||||
|
||||
export interface OldPageData {
|
||||
id: string;
|
||||
layout: RootSchema;
|
||||
componentsTree: RootSchema[];
|
||||
[dataAddon: string]: any;
|
||||
}
|
||||
|
||||
const pages = Object.assign(project, {
|
||||
setPages(pages: OldPageData[]) {
|
||||
if (!pages || !Array.isArray(pages) || pages.length === 0) {
|
||||
throw new Error('pages schema 不合法');
|
||||
}
|
||||
|
||||
if (pages[0].componentsTree[0]) {
|
||||
pages[0].componentsTree[0].componentName = 'Page';
|
||||
// FIXME
|
||||
pages[0].componentsTree[0].lifeCycles = {};
|
||||
pages[0].componentsTree[0].methods = {};
|
||||
}
|
||||
|
||||
project.load({
|
||||
version: '1.0.0',
|
||||
componentsMap: [],
|
||||
componentsTree: pages.map(page => page.layout),
|
||||
});
|
||||
componentsTree: pages[0].componentsTree,
|
||||
}, true);
|
||||
},
|
||||
addPage(data: OldPageData) {
|
||||
return project.open(data.layout);
|
||||
|
||||
@ -69,17 +69,7 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin
|
||||
newConfig.type = 'PanelDock';
|
||||
newConfig.area = 'left';
|
||||
newConfig.props.description = description || title;
|
||||
const {
|
||||
contents,
|
||||
hideTitleBar,
|
||||
tip,
|
||||
width,
|
||||
maxWidth,
|
||||
height,
|
||||
maxHeight,
|
||||
menu,
|
||||
isAction
|
||||
} = config;
|
||||
const { contents, hideTitleBar, tip, width, maxWidth, height, maxHeight, menu, isAction } = config;
|
||||
if (menu) {
|
||||
newConfig.props.title = menu;
|
||||
}
|
||||
@ -95,15 +85,16 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin
|
||||
};
|
||||
|
||||
if (contents && Array.isArray(contents)) {
|
||||
newConfig.content = contents.map(({ title, content, tip }) => {
|
||||
newConfig.content = contents.map(({ title, content, tip }, index) => {
|
||||
return {
|
||||
type: "Panel",
|
||||
type: 'Panel',
|
||||
name: typeof title === 'string' ? title : `${name}:${index}`,
|
||||
content,
|
||||
props: {
|
||||
title,
|
||||
help: tip,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -162,7 +153,11 @@ const dockPane = Object.assign(skeleton.leftArea, {
|
||||
return;
|
||||
}
|
||||
const name = item.name || item;
|
||||
skeleton.getPanel(name)?.active();
|
||||
const pane = skeleton.getPanel(name);
|
||||
if (!pane) {
|
||||
console.warn(`Could not find pane with name ${name}`);
|
||||
}
|
||||
pane?.active();
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
17
packages/vision-preset/src/project.ts
Normal file
17
packages/vision-preset/src/project.ts
Normal file
@ -0,0 +1,17 @@
|
||||
class Project {
|
||||
private schema: any;
|
||||
|
||||
constructor() {
|
||||
this.schema = {};
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
setSchema(schema: any) {
|
||||
this.schema = schema;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Project();
|
||||
@ -2,11 +2,12 @@ import { Component } from 'react';
|
||||
import { EventEmitter } from 'events';
|
||||
import { fromJS, Iterable, Map as IMMap } from 'immutable';
|
||||
import logger from '@ali/vu-logger';
|
||||
import { uniqueId, cloneDeep, isDataEqual, combineInitial, Transducer } from '@ali/ve-utils';
|
||||
import { cloneDeep, isDataEqual, combineInitial, Transducer } from '@ali/ve-utils';
|
||||
import I18nUtil from '@ali/ve-i18n-util';
|
||||
import { getSetter } from '@ali/lowcode-editor-core';
|
||||
import { editor } from './editor';
|
||||
import { OldPropConfig, DISPLAY_TYPE } from './bundle/upgrade-metadata';
|
||||
import { uniqueId } from '@ali/lowcode-utils';
|
||||
|
||||
type IPropConfig = OldPropConfig;
|
||||
|
||||
@ -108,7 +109,7 @@ export default class Prop implements IVariableSettable {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
this.id = uniqueId(null as any, 'prop', 'engine-prop');
|
||||
this.id = uniqueId('prop');
|
||||
|
||||
if (typeof config.setter === 'string') {
|
||||
config.setter = getSetter(config.setter)?.component as any;
|
||||
|
||||
17
packages/vision-preset/src/symbols.ts
Normal file
17
packages/vision-preset/src/symbols.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export class SymbolManager {
|
||||
private symbolMap: { [symbolName: string]: symbol } = {};
|
||||
|
||||
public create(name: string): symbol {
|
||||
if (this.symbolMap[name]) {
|
||||
return this.symbolMap[name];
|
||||
}
|
||||
this.symbolMap[name] = Symbol(name);
|
||||
return this.symbolMap[name];
|
||||
}
|
||||
|
||||
public get(name: string) {
|
||||
return this.symbolMap[name];
|
||||
}
|
||||
}
|
||||
|
||||
export default new SymbolManager();
|
||||
278
packages/vision-preset/src/viewport.ts
Normal file
278
packages/vision-preset/src/viewport.ts
Normal file
@ -0,0 +1,278 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const domReady = require('domready');
|
||||
import Flags from './flags';
|
||||
|
||||
function enterFullscreen() {
|
||||
const elem = document.documentElement;
|
||||
if (elem.requestFullscreen) {
|
||||
elem.requestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
function exitFullscreen() {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
function isFullscreen() {
|
||||
return document.fullscreen || false;
|
||||
}
|
||||
|
||||
interface IStyleResourceConfig {
|
||||
media?: string;
|
||||
type?: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
class StyleResource {
|
||||
config: IStyleResourceConfig;
|
||||
styleElement: HTMLStyleElement;
|
||||
mounted: boolean;
|
||||
inited: boolean;
|
||||
|
||||
constructor(config: IStyleResourceConfig) {
|
||||
this.config = config || {};
|
||||
}
|
||||
|
||||
matchDevice(device: string) {
|
||||
const media = this.config.media;
|
||||
|
||||
if (!media || media === 'ALL' || media === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return media.toUpperCase() === device.toUpperCase();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inited = true;
|
||||
|
||||
const { type, content } = this.config;
|
||||
|
||||
let styleElement;
|
||||
if (type === 'URL') {
|
||||
styleElement = document.createElement('link');
|
||||
styleElement.href = content || '';
|
||||
styleElement.rel = 'stylesheet';
|
||||
} else {
|
||||
styleElement = document.createElement('style');
|
||||
styleElement.setAttribute('type', 'text/css');
|
||||
if (styleElement.styleSheet) {
|
||||
styleElement.styleSheet.cssText = content;
|
||||
} else {
|
||||
styleElement.appendChild(document.createTextNode(content || ''));
|
||||
}
|
||||
}
|
||||
this.styleElement = styleElement;
|
||||
}
|
||||
|
||||
apply() {
|
||||
if (this.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.init();
|
||||
document.head.appendChild(this.styleElement);
|
||||
this.mounted = true;
|
||||
}
|
||||
|
||||
unmount() {
|
||||
if (!this.mounted) {
|
||||
return;
|
||||
}
|
||||
document.head.removeChild(this.styleElement);
|
||||
this.mounted = false;
|
||||
}
|
||||
}
|
||||
|
||||
export class Viewport {
|
||||
preview: boolean;
|
||||
focused: boolean;
|
||||
slateFixed: boolean;
|
||||
emitter: EventEmitter;
|
||||
device: string;
|
||||
focusTarget: any;
|
||||
cssResourceSet: StyleResource[];
|
||||
|
||||
constructor() {
|
||||
this.preview = false;
|
||||
this.emitter = new EventEmitter();
|
||||
document.addEventListener('webkitfullscreenchange', () => {
|
||||
this.emitter.emit('fullscreenchange', this.isFullscreen());
|
||||
});
|
||||
domReady(() => this.applyMediaCSS());
|
||||
}
|
||||
|
||||
setFullscreen(flag: boolean) {
|
||||
const fullscreen = this.isFullscreen();
|
||||
if (fullscreen && !flag) {
|
||||
exitFullscreen();
|
||||
} else if (!fullscreen && flag) {
|
||||
enterFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
if (this.isFullscreen()) {
|
||||
exitFullscreen();
|
||||
} else {
|
||||
enterFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
isFullscreen() {
|
||||
return isFullscreen();
|
||||
}
|
||||
|
||||
setFocus(flag: boolean) {
|
||||
if (this.focused && !flag) {
|
||||
this.focused = false;
|
||||
Flags.remove('view-focused');
|
||||
this.emitter.emit('focuschange', false);
|
||||
} else if (!this.focused && flag) {
|
||||
this.focused = true;
|
||||
Flags.add('view-focused');
|
||||
this.emitter.emit('focuschange', true);
|
||||
}
|
||||
}
|
||||
|
||||
setFocusTarget(focusTarget: any) {
|
||||
this.focusTarget = focusTarget;
|
||||
}
|
||||
|
||||
returnFocus() {
|
||||
if (this.focusTarget) {
|
||||
this.focusTarget.focus();
|
||||
}
|
||||
}
|
||||
|
||||
isFocus() {
|
||||
return this.focused;
|
||||
}
|
||||
|
||||
setPreview(flag: boolean) {
|
||||
if (this.preview && !flag) {
|
||||
this.preview = false;
|
||||
Flags.setPreviewMode(false);
|
||||
this.emitter.emit('preview', false);
|
||||
this.changeViewport();
|
||||
} else if (!this.preview && flag) {
|
||||
this.preview = true;
|
||||
Flags.setPreviewMode(true);
|
||||
this.emitter.emit('preview', true);
|
||||
this.changeViewport();
|
||||
}
|
||||
}
|
||||
|
||||
togglePreview() {
|
||||
if (this.isPreview()) {
|
||||
this.setPreview(false);
|
||||
} else {
|
||||
this.setPreview(true);
|
||||
}
|
||||
}
|
||||
|
||||
isPreview() {
|
||||
return this.preview;
|
||||
}
|
||||
|
||||
setDevice(device = 'pc') {
|
||||
if (this.getDevice() !== device) {
|
||||
this.device = device;
|
||||
Flags.setSimulator(device);
|
||||
this.applyMediaCSS();
|
||||
this.emitter.emit('devicechange', device);
|
||||
this.changeViewport();
|
||||
}
|
||||
}
|
||||
|
||||
getDevice() {
|
||||
return this.device || 'pc';
|
||||
}
|
||||
|
||||
changeViewport() {
|
||||
this.emitter.emit('viewportchange', this.getViewport());
|
||||
}
|
||||
|
||||
getViewport() {
|
||||
return `${this.isPreview() ? 'preview' : 'design'}-${this.getDevice()}`;
|
||||
}
|
||||
|
||||
applyMediaCSS() {
|
||||
if (!document.head || !this.cssResourceSet) {
|
||||
return;
|
||||
}
|
||||
const device = this.getDevice();
|
||||
this.cssResourceSet.forEach((item) => {
|
||||
if (item.matchDevice(device)) {
|
||||
item.apply();
|
||||
} else {
|
||||
item.unmount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setGlobalCSS(resourceSet: IStyleResourceConfig[]) {
|
||||
if (this.cssResourceSet) {
|
||||
this.cssResourceSet.forEach((item) => {
|
||||
item.unmount();
|
||||
});
|
||||
}
|
||||
this.cssResourceSet = resourceSet.map((item: IStyleResourceConfig) => new StyleResource(item)).reverse();
|
||||
this.applyMediaCSS();
|
||||
}
|
||||
|
||||
setWithShell(shell: string) {
|
||||
Flags.setWithShell(shell);
|
||||
}
|
||||
|
||||
onFullscreenChange(func: () => any) {
|
||||
this.emitter.on('fullscreenchange', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('fullscreenchange', func);
|
||||
};
|
||||
}
|
||||
|
||||
onPreview(func: () => any) {
|
||||
this.emitter.on('preview', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('preview', func);
|
||||
};
|
||||
}
|
||||
|
||||
onDeviceChange(func: () => any) {
|
||||
this.emitter.on('devicechange', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('devicechange', func);
|
||||
};
|
||||
}
|
||||
|
||||
onSlateFixedChange(func: (flag: boolean) => any) {
|
||||
this.emitter.on('slatefixed', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('slatefixed', func);
|
||||
};
|
||||
}
|
||||
|
||||
onViewportChange(func: () => any) {
|
||||
this.emitter.on('viewportchange', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('viewportchange', func);
|
||||
};
|
||||
}
|
||||
|
||||
onFocusChange(func: (flag: boolean) => any) {
|
||||
this.emitter.on('focuschange', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('focuschange', func);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new Viewport();
|
||||
@ -92,7 +92,8 @@ html.engine-blur #engine {
|
||||
.next-checkbox-group,.next-date-picker,.next-input,.next-month-picker,
|
||||
.next-number-picker,.next-radio-group,.next-range,.next-range-picker,
|
||||
.next-rating,.next-select,.next-switch,.next-time-picker,.next-upload,
|
||||
.next-year-picker {
|
||||
.next-year-picker,
|
||||
.next-breadcrumb-item,.next-calendar-header,.next-calendar-table {
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
}
|
||||
@ -100,3 +101,9 @@ html.engine-blur #engine {
|
||||
.lc-left-float-pane {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
html.engine-preview-mode {
|
||||
.lc-left-area, .lc-right-area {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user