mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-02-27 20:30:28 +00:00
big change
This commit is contained in:
parent
b486a8419c
commit
e801a4c47a
@ -15,7 +15,9 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ali/lowcode-globals": "^0.9.3",
|
||||
"@ali/lowcode-editor-core": "^0.8.0",
|
||||
"@ali/lowcode-utils": "^0.8.0",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16",
|
||||
"react-dom": "^16.7.0"
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
import { computed } from '@ali/lowcode-editor-core';
|
||||
import { Node, ParentalNode } from './document';
|
||||
import { Designer } from './designer';
|
||||
import { intl } from './locale';
|
||||
import { intlNode } from './locale';
|
||||
import { IconContainer } from './icons/container';
|
||||
import { IconPage } from './icons/page';
|
||||
import { IconComponent } from './icons/component';
|
||||
@ -235,6 +235,38 @@ function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMe
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export interface MetadataTransducer {
|
||||
(prev: TransformedComponentMetadata): TransformedComponentMetadata;
|
||||
/**
|
||||
* 0 - 9 system
|
||||
* 10 - 99 builtin-plugin
|
||||
* 100 - app & plugin
|
||||
*/
|
||||
level?: number;
|
||||
/**
|
||||
* use to replace TODO
|
||||
*/
|
||||
id?: string;
|
||||
}
|
||||
const metadataTransducers: MetadataTransducer[] = [];
|
||||
|
||||
export function registerMetadataTransducer(transducer: MetadataTransducer, level: number = 100, id?: string) {
|
||||
transducer.level = level;
|
||||
transducer.id = id;
|
||||
const i = metadataTransducers.findIndex((item) => item.level != null && item.level > level);
|
||||
if (i < 0) {
|
||||
metadataTransducers.push(transducer);
|
||||
} else {
|
||||
metadataTransducers.splice(i, 0, transducer);
|
||||
}
|
||||
}
|
||||
|
||||
export function getRegisteredMetadataTransducers(): MetadataTransducer[] {
|
||||
return metadataTransducers;
|
||||
}
|
||||
|
||||
|
||||
registerMetadataTransducer((metadata) => {
|
||||
const { configure, componentName } = metadata;
|
||||
const { component = {} } = configure;
|
||||
@ -280,7 +312,7 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
name: 'remove',
|
||||
content: {
|
||||
icon: IconRemove,
|
||||
title: intl('remove'),
|
||||
title: intlNode('remove'),
|
||||
action(node: Node) {
|
||||
node.remove();
|
||||
},
|
||||
@ -291,7 +323,7 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
name: 'copy',
|
||||
content: {
|
||||
icon: IconClone,
|
||||
title: intl('copy'),
|
||||
title: intlNode('copy'),
|
||||
action(node: Node) {
|
||||
// node.remove();
|
||||
},
|
||||
@ -309,33 +341,3 @@ export function removeBuiltinComponentAction(name: string) {
|
||||
export function addBuiltinComponentAction(action: ComponentAction) {
|
||||
builtinComponentActions.push(action);
|
||||
}
|
||||
|
||||
export interface MetadataTransducer {
|
||||
(prev: TransformedComponentMetadata): TransformedComponentMetadata;
|
||||
/**
|
||||
* 0 - 9 system
|
||||
* 10 - 99 builtin-plugin
|
||||
* 100 - app & plugin
|
||||
*/
|
||||
level?: number;
|
||||
/**
|
||||
* use to replace TODO
|
||||
*/
|
||||
id?: string;
|
||||
}
|
||||
const metadataTransducers: MetadataTransducer[] = [];
|
||||
|
||||
export function registerMetadataTransducer(transducer: MetadataTransducer, level: number = 100, id?: string) {
|
||||
transducer.level = level;
|
||||
transducer.id = id;
|
||||
const i = metadataTransducers.findIndex((item) => item.level != null && item.level > level);
|
||||
if (i < 0) {
|
||||
metadataTransducers.push(transducer);
|
||||
} else {
|
||||
metadataTransducers.splice(i, 0, transducer);
|
||||
}
|
||||
}
|
||||
|
||||
export function getRegisteredMetadataTransducers(): MetadataTransducer[] {
|
||||
return metadataTransducers;
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
[
|
||||
"build-plugin-component",
|
||||
{
|
||||
"filename": "globals",
|
||||
"library": "LCEGlobals",
|
||||
"filename": "core",
|
||||
"library": "LCECore",
|
||||
"libraryTarget": "umd"
|
||||
}
|
||||
],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-globals",
|
||||
"version": "0.9.3",
|
||||
"version": "0.8.6",
|
||||
"description": "Globals api for Ali lowCode engine",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
@ -27,13 +27,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.19.16",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"@ali/lowcode-utils": "^0.8.0",
|
||||
"@recore/obx": "^1.0.8",
|
||||
"@recore/obx-react": "^1.0.7",
|
||||
"classnames": "^2.2.6",
|
||||
"power-di": "^2.2.4",
|
||||
"debug": "^4.1.1",
|
||||
"intl-messageformat": "^8.3.1",
|
||||
"lodash": "^4.17.15",
|
||||
"store": "^2.0.12",
|
||||
"react": "^16",
|
||||
"react-dom": "^16.7.0"
|
||||
@ -44,8 +45,6 @@
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/react": "^16",
|
||||
"@types/react-dom": "^16",
|
||||
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/store": "^2.0.2",
|
||||
"build-plugin-component": "^0.2.11",
|
||||
"build-plugin-fusion": "^0.1.0",
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export * from './setter';
|
||||
export * from './ioc-context';
|
||||
export * from './tip';
|
||||
export * from '../widgets/tip/tip';
|
||||
|
||||
@ -20,6 +20,8 @@ export type GetReturnType<T, ClsType> = T extends undefined
|
||||
|
||||
const NOT_FOUND = Symbol.for('not_found');
|
||||
|
||||
import * as utils from './utils';
|
||||
|
||||
export class Editor extends EventEmitter implements IEditor {
|
||||
/**
|
||||
* Ioc Container
|
||||
@ -32,6 +34,8 @@ export class Editor extends EventEmitter implements IEditor {
|
||||
return globalLocale.getLocale();
|
||||
}
|
||||
|
||||
readonly utils = utils;
|
||||
|
||||
constructor(readonly config: EditorConfig = {}, readonly components: PluginClassSet = {}) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { intl } from '@ali/lowcode-editor-core';
|
||||
import { TipConfig } from '@ali/lowcode-types';
|
||||
import { intl } from '../../intl';
|
||||
import { resolvePosition } from './utils';
|
||||
import { tipHandler } from './tip-handler';
|
||||
|
||||
|
||||
@ -20,13 +20,14 @@
|
||||
],
|
||||
"author": "xiayang.xy",
|
||||
"dependencies": {
|
||||
"@ali/lowcode-editor-core": "^0.8.6",
|
||||
"@alifd/next": "^1.x",
|
||||
"prop-types": "^15.5.8",
|
||||
"@ali/lowcode-editor-core": "^0.8.6",
|
||||
"@ali/lowcode-designer": "^0.9.2",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"@ali/lowcode-utils": "^0.8.0",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"store": "^2.0.12"
|
||||
"react-dom": "^16.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.3",
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../variables.less';
|
||||
|
||||
@x-gap: 10px;
|
||||
@y-gap: 8px;
|
||||
|
||||
@ -57,7 +55,7 @@
|
||||
&.lc-block-field, &.lc-accordion-field {
|
||||
display: block;
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid var(--color-line-normal, @dark-alpha-2);
|
||||
border-top: 1px solid var(--color-line-normal);
|
||||
}
|
||||
> .lc-field-head {
|
||||
padding-left: @x-gap;
|
||||
@ -66,8 +64,8 @@
|
||||
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, @dark-alpha-2);
|
||||
color: var(--color-title, @white-alpha-2);
|
||||
border-bottom: 1px solid var(--color-line-normal);
|
||||
color: var(--color-title);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@ -76,7 +74,7 @@
|
||||
}
|
||||
|
||||
+ .lc-inline-field {
|
||||
border-top: 1px solid var(--color-line-normal, @dark-alpha-2);
|
||||
border-top: 1px solid var(--color-line-normal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,3 @@
|
||||
export * from './settings-pane';
|
||||
import './transducers/register';
|
||||
import './register';
|
||||
import './style.less';
|
||||
import SettingsMainView from './settings-primary-view';
|
||||
|
||||
export default SettingsMainView;
|
||||
export * from './settings-primary-pane';
|
||||
export * from './settings-pane';
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "@ali/lowcode-plugin-settings-pane",
|
||||
"version": "0.8.10",
|
||||
"description": "Settings pane for Ali lowCode engine",
|
||||
"files": [
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"scripts": {
|
||||
"build": "build-scripts build --skip-demo",
|
||||
"test": "ava",
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^0.9.2",
|
||||
"@ali/lowcode-editor-core": "^0.8.5",
|
||||
"@ali/lowcode-globals": "^0.9.2",
|
||||
"@ali/lowcode-plugin-outline-pane": "^0.8.8",
|
||||
"@ali/ve-stage-box": "^4.0.0",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/react": "^16",
|
||||
"build-plugin-component": "^0.2.10",
|
||||
"build-plugin-fusion": "^0.1.1",
|
||||
"build-plugin-moment-locales": "^0.1.0"
|
||||
},
|
||||
"ava": {
|
||||
"compileEnhancements": false,
|
||||
"snapshotDir": "test/fixtures/__snapshots__",
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
]
|
||||
},
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npm.alibaba-inc.com"
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ import { SettingsPane } from './settings-pane';
|
||||
import { createIcon } from '@ali/lowcode-utils';
|
||||
|
||||
@observer
|
||||
export default class SettingsMainView extends Component<{ editor: Editor }> {
|
||||
export class SettingsPrimaryPane extends Component<{ editor: Editor }> {
|
||||
private main = new SettingsMain(this.props.editor);
|
||||
|
||||
shouldComponentUpdate() {
|
||||
@ -1,9 +0,0 @@
|
||||
import { registerMetadataTransducer } from '@ali/lowcode-designer';
|
||||
import parseProps from './parse-props';
|
||||
import addonCombine from './addon-combine';
|
||||
|
||||
// parseProps
|
||||
registerMetadataTransducer(parseProps, 10, 'parse-props');
|
||||
|
||||
// addon/platform custom
|
||||
registerMetadataTransducer(addonCombine, 11, 'combine-props');
|
||||
@ -1,87 +0,0 @@
|
||||
import { ReactNode, createElement } from 'react';
|
||||
import { obx } from '@ali/lowcode-editor-core';
|
||||
import { uniqueId, createContent } from '@ali/lowcode-utils';
|
||||
import { DockConfig } from "./types";
|
||||
import { Skeleton } from './skeleton';
|
||||
import { DockView, WidgetView } from './components/widget-views';
|
||||
import { IWidget } from './widget/widget';
|
||||
|
||||
/**
|
||||
* 带图标(主要)/标题(次要)的扩展
|
||||
*/
|
||||
export default class Dock implements IWidget {
|
||||
readonly isWidget = true;
|
||||
readonly id = uniqueId('dock');
|
||||
readonly name: string;
|
||||
readonly align?: string;
|
||||
|
||||
@obx.ref private _visible: boolean = true;
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
get content(): ReactNode {
|
||||
return createElement(WidgetView, {
|
||||
widget: this,
|
||||
key: this.id,
|
||||
});
|
||||
}
|
||||
|
||||
private inited: boolean = false;
|
||||
private _body: ReactNode;
|
||||
get body() {
|
||||
if (this.inited) {
|
||||
return this._body;
|
||||
}
|
||||
|
||||
const { props, content, contentProps } = this.config;
|
||||
|
||||
if (content) {
|
||||
this._body = createContent(content, {
|
||||
...contentProps,
|
||||
config: this.config,
|
||||
editor: this.skeleton.editor,
|
||||
});
|
||||
} else {
|
||||
this._body = createElement(DockView, props);
|
||||
}
|
||||
return this._body;
|
||||
}
|
||||
|
||||
constructor(readonly skeleton: Skeleton, readonly config: DockConfig) {
|
||||
const { props = {}, name } = config;
|
||||
this.name = name;
|
||||
this.align = props.align;
|
||||
}
|
||||
|
||||
setVisible(flag: boolean) {
|
||||
if (flag === this._visible) {
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
this._visible = true;
|
||||
} else if (this.inited) {
|
||||
this._visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setVisible(false);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.setVisible(!this._visible);
|
||||
}
|
||||
}
|
||||
@ -1 +1,7 @@
|
||||
export { Workbench } from './layouts/workbench';
|
||||
export * from './skeleton';
|
||||
export * from './types';
|
||||
export * from './components/settings';
|
||||
export * from './components/field';
|
||||
|
||||
import './register-defaults';
|
||||
|
||||
@ -1,166 +0,0 @@
|
||||
import { createElement, ReactNode } from 'react';
|
||||
import { TitleContent } from '@ali/lowcode-types';
|
||||
import { uniqueId, createContent } from '@ali/lowcode-utils';
|
||||
import { obx } from '@ali/lowcode-editor-core';
|
||||
import WidgetContainer from './widget/widget-container';
|
||||
import { PanelConfig, HelpTipConfig } from './types';
|
||||
import { TitledPanelView, TabsPanelView, PanelView } from './components/widget-views';
|
||||
import { Skeleton } from './skeleton';
|
||||
import { composeTitle } from './widget/utils';
|
||||
import { IWidget } from './widget/widget';
|
||||
|
||||
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;
|
||||
get actived(): boolean {
|
||||
return this._actived;
|
||||
}
|
||||
|
||||
get visible(): boolean {
|
||||
if (this.parent?.visible) {
|
||||
return this._actived;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
readonly isPanel = true;
|
||||
|
||||
private _body?: ReactNode;
|
||||
get body() {
|
||||
this.initBody();
|
||||
return this._body;
|
||||
}
|
||||
|
||||
get content(): ReactNode {
|
||||
if (this.plain) {
|
||||
return createElement(PanelView, {
|
||||
panel: this,
|
||||
key: this.id,
|
||||
});
|
||||
}
|
||||
return createElement(TitledPanelView, { panel: this, key: this.id });
|
||||
}
|
||||
|
||||
readonly title: TitleContent;
|
||||
readonly help?: HelpTipConfig;
|
||||
private plain: boolean = false;
|
||||
|
||||
private container?: WidgetContainer<Panel, PanelConfig>;
|
||||
private parent?: WidgetContainer;
|
||||
|
||||
constructor(readonly skeleton: Skeleton, readonly config: PanelConfig) {
|
||||
const { name, content, props = {} } = config;
|
||||
const { hideTitleBar, title, icon, description, help, shortcut } = props;
|
||||
this.name = name;
|
||||
this.id = uniqueId(`pane:${name}$`);
|
||||
this.title = composeTitle(title || name, icon, description);
|
||||
this.plain = hideTitleBar || !title;
|
||||
this.help = help;
|
||||
if (Array.isArray(content)) {
|
||||
this.container = this.skeleton.createContainer(
|
||||
name,
|
||||
(item) => {
|
||||
if (isPanel(item)) {
|
||||
return item;
|
||||
}
|
||||
return this.skeleton.createPanel(item);
|
||||
},
|
||||
true,
|
||||
() => this.visible,
|
||||
true,
|
||||
);
|
||||
content.forEach((item) => this.add(item));
|
||||
}
|
||||
// todo: process shortcut
|
||||
}
|
||||
|
||||
private initBody() {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
this.inited = true;
|
||||
if (this.container) {
|
||||
this._body = createElement(TabsPanelView, {
|
||||
container: this.container,
|
||||
});
|
||||
} else {
|
||||
const { content, contentProps } = this.config;
|
||||
this._body = createContent(content, {
|
||||
...contentProps,
|
||||
editor: this.skeleton.editor,
|
||||
config: this.config,
|
||||
panel: this,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setParent(parent: WidgetContainer) {
|
||||
if (parent === this.parent) {
|
||||
return;
|
||||
}
|
||||
if (this.parent) {
|
||||
this.parent.remove(this);
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
add(item: Panel | PanelConfig) {
|
||||
return this.container?.add(item);
|
||||
}
|
||||
|
||||
getPane(name: string): Panel | null {
|
||||
return this.container?.get(name) || null;
|
||||
}
|
||||
|
||||
remove(item: Panel | string) {
|
||||
return this.container?.remove(item);
|
||||
}
|
||||
|
||||
active(item?: Panel | string | null) {
|
||||
this.container?.active(item);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
setActive(flag: boolean) {
|
||||
if (flag === this._actived) {
|
||||
// TODO: 如果移动到另外一个 container,会有问题
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
if (!this.inited) {
|
||||
this.initBody();
|
||||
}
|
||||
this._actived = true;
|
||||
this.parent?.active(this);
|
||||
} else if (this.inited) {
|
||||
this._actived = false;
|
||||
this.parent?.unactive(this);
|
||||
}
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.setActive(!this._actived);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setActive(false);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
export function isPanel(obj: any): obj is Panel {
|
||||
return obj && obj.isPanel;
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
import { registerSetter } from '@ali/lowcode-editor-core';
|
||||
import ArraySetter from '../array-setter';
|
||||
import ObjectSetter from '../object-setter';
|
||||
import MixedSetter from '../mixed-setter';
|
||||
import { registerMetadataTransducer } from '@ali/lowcode-designer';
|
||||
import ArraySetter from './components/array-setter';
|
||||
import ObjectSetter from './components/object-setter';
|
||||
import MixedSetter from './components/mixed-setter';
|
||||
import { isPlainObject } from '@ali/lowcode-utils';
|
||||
import parseProps from './transducers/parse-props';
|
||||
import addonCombine from './transducers/addon-combine';
|
||||
|
||||
registerSetter('ArraySetter', {
|
||||
component: ArraySetter,
|
||||
@ -28,3 +31,9 @@ registerSetter('ObjectSetter', {
|
||||
recommend: true,
|
||||
});
|
||||
registerSetter('MixedSetter', MixedSetter);
|
||||
|
||||
// parseProps
|
||||
registerMetadataTransducer(parseProps, 10, 'parse-props');
|
||||
|
||||
// addon/platform custom
|
||||
registerMetadataTransducer(addonCombine, 11, 'combine-props');
|
||||
@ -1,4 +1,4 @@
|
||||
import { IconType, TitleContent, isI18nData, TipContent } from '@ali/lowcode-globals';
|
||||
import { IconType, TitleContent, isI18nData, TipContent } from '@ali/lowcode-types';
|
||||
import { isValidElement } from 'react';
|
||||
|
||||
export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipContent, tipAsTitle?: boolean) {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { obx, hasOwnProperty, computed } from '@ali/lowcode-globals';
|
||||
import { obx, computed } from '@ali/lowcode-editor-core';
|
||||
import { isPanel } from './panel';
|
||||
import { hasOwnProperty } from '@ali/lowcode-utils';
|
||||
export interface WidgetItem {
|
||||
name: string;
|
||||
}
|
||||
|
||||
30
packages/editor/src/editor.ts
Normal file
30
packages/editor/src/editor.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||
import { Designer } from '@ali/lowcode-designer';
|
||||
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
||||
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
|
||||
|
||||
export const editor = new Editor();
|
||||
globalContext.register(editor, Editor);
|
||||
|
||||
export const skeleton = new Skeleton(editor);
|
||||
editor.set(Skeleton, skeleton);
|
||||
|
||||
export const designer = new Designer({ editor: editor });
|
||||
editor.set(Designer, designer);
|
||||
|
||||
skeleton.add({
|
||||
area: 'mainArea',
|
||||
name: 'designer',
|
||||
type: 'Widget',
|
||||
content: DesignerPlugin,
|
||||
});
|
||||
skeleton.add({
|
||||
area: 'rightArea',
|
||||
name: 'settingsPane',
|
||||
type: 'Panel',
|
||||
content: SettingsPrimaryPane,
|
||||
});
|
||||
|
||||
export * from '@ali/lowcode-types';
|
||||
export * from '@ali/lowcode-designer';
|
||||
export * from '@ali/lowcode-editor-skeleton'
|
||||
@ -25,6 +25,7 @@
|
||||
"@ali/iceluna-sdk": "^1.0.6-beta.6",
|
||||
"@ali/lowcode-designer": "^0.9.3",
|
||||
"@ali/lowcode-editor-core": "^0.8.6",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"@alifd/next": "^1.19.19",
|
||||
"react": "^16.8.1"
|
||||
},
|
||||
|
||||
@ -19,13 +19,10 @@ export interface IState {
|
||||
componentList: object[];
|
||||
}
|
||||
|
||||
export default class ComponentListPlugin extends PureComponent<
|
||||
PluginProps,
|
||||
IState
|
||||
> {
|
||||
export default class ComponentListPlugin extends PureComponent<PluginProps, IState> {
|
||||
static displayName = 'LowcodeComponentListPlugin';
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
@ -50,15 +47,15 @@ export default class ComponentListPlugin extends PureComponent<
|
||||
}
|
||||
}
|
||||
|
||||
transformMaterial = (componentList): any => {
|
||||
return componentList.map(category => {
|
||||
transformMaterial = (componentList: any): any => {
|
||||
return componentList.map((category: any) => {
|
||||
return {
|
||||
name: category.title,
|
||||
items: category.children.map(comp => {
|
||||
items: category.children.map((comp: any) => {
|
||||
return {
|
||||
...comp,
|
||||
name: comp.componentName,
|
||||
snippets: comp.snippets.map(snippet => {
|
||||
snippets: comp.snippets.map((snippet: any) => {
|
||||
return {
|
||||
name: snippet.title,
|
||||
screenshot: snippet.screenshot,
|
||||
@ -76,7 +73,7 @@ export default class ComponentListPlugin extends PureComponent<
|
||||
const assets = editor.get('assets') || {};
|
||||
const list: string[] = [];
|
||||
const libs: LibrayInfo[] = [];
|
||||
Object.values(assets.packages).forEach((item): void => {
|
||||
Object.values(assets.packages).forEach((item: any): void => {
|
||||
list.push(item.library);
|
||||
libs.push({
|
||||
label: item.title,
|
||||
@ -100,7 +97,7 @@ export default class ComponentListPlugin extends PureComponent<
|
||||
});
|
||||
|
||||
editor.set('dndHelper', {
|
||||
handleResourceDragStart: function(ev, tagName, schema) {
|
||||
handleResourceDragStart: function(ev: any, tagName: any, schema: any) {
|
||||
const designer = editor.get(Designer);
|
||||
if (designer) {
|
||||
designer.dragon.boost(
|
||||
@ -125,18 +122,16 @@ export default class ComponentListPlugin extends PureComponent<
|
||||
const { searchKey, currentLib, componentList } = this.state;
|
||||
const libs = currentLib.split(',');
|
||||
return (componentList || [])
|
||||
.map(cate => {
|
||||
.map((cate: any) => {
|
||||
return {
|
||||
...cate,
|
||||
items: (cate.items || []).filter(item => {
|
||||
let libFlag = libs.some(lib => lib == item.library);
|
||||
items: (cate.items || []).filter((item: any) => {
|
||||
let libFlag = libs.some((lib) => lib == item.library);
|
||||
|
||||
let keyFlag = true;
|
||||
if (searchKey) {
|
||||
keyFlag =
|
||||
`${item.name || ''} ${item.title || ''}`
|
||||
.toLowerCase()
|
||||
.indexOf(searchKey.trim().toLowerCase()) >= 0;
|
||||
`${item.name || ''} ${item.title || ''}`.toLowerCase().indexOf(searchKey.trim().toLowerCase()) >= 0;
|
||||
} else {
|
||||
keyFlag = item.is_show === undefined || !!(item.is_show == 1);
|
||||
}
|
||||
@ -144,7 +139,7 @@ export default class ComponentListPlugin extends PureComponent<
|
||||
}),
|
||||
};
|
||||
})
|
||||
.filter(cate => {
|
||||
.filter((cate) => {
|
||||
return cate.items && cate.items.length > 0;
|
||||
});
|
||||
};
|
||||
@ -159,10 +154,10 @@ export default class ComponentListPlugin extends PureComponent<
|
||||
</div>
|
||||
<Search
|
||||
shape="simple"
|
||||
size="small"
|
||||
size="medium"
|
||||
className="search"
|
||||
placeholder="请输入关键词"
|
||||
onChange={this.searchAction}
|
||||
onChange={this.searchAction as any}
|
||||
onSearch={this.searchAction}
|
||||
hasClear
|
||||
/>
|
||||
|
||||
1
packages/plugin-components-pane/src/module.d.ts
vendored
Normal file
1
packages/plugin-components-pane/src/module.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module "@ali/iceluna-comp-material-show";
|
||||
@ -19,7 +19,8 @@
|
||||
],
|
||||
"author": "zude.hzd",
|
||||
"dependencies": {
|
||||
"@ali/lowcode-editor-core": "^0.8.6",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"@ali/lowcode-editor-core": "^0.8.0",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { Component, isValidElement, ReactElement, ReactNode } from 'react';
|
||||
import { Dialog, Search, Input } from '@alifd/next';
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import { PluginProps } from '@ali/lowcode-types';
|
||||
import './index.scss';
|
||||
|
||||
export default class EventBindDialog extends Component<{
|
||||
editor:Editor,
|
||||
}> {
|
||||
export default class EventBindDialog extends Component<PluginProps> {
|
||||
private eventList: any[] = [
|
||||
{
|
||||
name: 'getData',
|
||||
@ -24,29 +22,29 @@ export default class EventBindDialog extends Component<{
|
||||
},
|
||||
];
|
||||
|
||||
state = {
|
||||
visiable:false,
|
||||
state: any = {
|
||||
visiable: false,
|
||||
selectedEventName: '',
|
||||
eventName: '',
|
||||
};
|
||||
|
||||
openDialog = (bindEventName:String) => {
|
||||
openDialog = (bindEventName: String) => {
|
||||
this.setState({
|
||||
visiable:true,
|
||||
eventName:bindEventName
|
||||
})
|
||||
}
|
||||
visiable: true,
|
||||
eventName: bindEventName,
|
||||
});
|
||||
};
|
||||
|
||||
closeDialog = () => {
|
||||
this.setState({
|
||||
visiable:false
|
||||
})
|
||||
}
|
||||
visiable: false,
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount (){
|
||||
const {editor,config} = this.props;
|
||||
editor.on(`${config.pluginKey}.openDialog`,(bindEventName:String)=>{
|
||||
this.openDialog(bindEventName)
|
||||
componentDidMount() {
|
||||
const { editor, config } = this.props;
|
||||
editor.on(`${config.pluginKey}.openDialog`, (bindEventName: String) => {
|
||||
this.openDialog(bindEventName);
|
||||
});
|
||||
}
|
||||
|
||||
@ -89,22 +87,28 @@ export default class EventBindDialog extends Component<{
|
||||
onSearchEvent = (searchEventName: String) => {};
|
||||
|
||||
onOk = () => {
|
||||
const {editor} = this.props;
|
||||
editor.emit('event-setter.bindEvent',this.state.eventName);
|
||||
const { editor } = this.props;
|
||||
editor.emit('event-setter.bindEvent', this.state.eventName);
|
||||
// 选中的是新建事件
|
||||
if (this.state.selectedEventName == ''){
|
||||
editor.emit('sourceEditor.addFunction',{
|
||||
functionName:this.state.eventName
|
||||
})
|
||||
if (this.state.selectedEventName == '') {
|
||||
editor.emit('sourceEditor.addFunction', {
|
||||
functionName: this.state.eventName,
|
||||
});
|
||||
}
|
||||
|
||||
this.closeDialog();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { selectedEventName, eventName,visiable} = this.state;
|
||||
const { selectedEventName, eventName, visiable } = this.state;
|
||||
return (
|
||||
<Dialog visible={visiable} title="事件绑定" onClose={this.closeDialog} onCancel={this.closeDialog} onOk={this.onOk}>
|
||||
<Dialog
|
||||
visible={visiable}
|
||||
title="事件绑定"
|
||||
onClose={this.closeDialog}
|
||||
onCancel={this.closeDialog}
|
||||
onOk={this.onOk}
|
||||
>
|
||||
<div className="event-dialog-body">
|
||||
<div className="dialog-left-container">
|
||||
<div className="dialog-small-title">事件选择</div>
|
||||
|
||||
@ -16,7 +16,8 @@
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^0.9.3",
|
||||
"@ali/lowcode-editor-core": "^0.8.6",
|
||||
"@ali/lowcode-globals": "^0.9.3",
|
||||
"@ali/lowcode-utils": "^0.8.0",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16",
|
||||
|
||||
30
packages/plugin-outline-pane/src/views/backup-pane.tsx
Normal file
30
packages/plugin-outline-pane/src/views/backup-pane.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { PluginProps } from '@ali/lowcode-types';
|
||||
import OutlinePane from './pane';
|
||||
|
||||
export class OutlineBackupPane extends PureComponent<PluginProps> {
|
||||
state = {
|
||||
outlineInited: false,
|
||||
};
|
||||
private dispose = this.props.main.onceOutlineVisible(() => {
|
||||
this.setState({
|
||||
outlineInited: true,
|
||||
});
|
||||
});
|
||||
componentWillUnmount() {
|
||||
this.dispose();
|
||||
}
|
||||
render() {
|
||||
if (!this.state.outlineInited) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<OutlinePane
|
||||
editor={this.props.main.editor}
|
||||
config={{
|
||||
name: '__IN_SETTINGS__',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,6 @@ export default class OutlinePane extends Component<{ config: any; editor: any; i
|
||||
}
|
||||
|
||||
render() {
|
||||
console.info(this.props);
|
||||
const tree = this.main.currentTree;
|
||||
|
||||
if (!tree) {
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="0.8.10"></a>
|
||||
## [0.8.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-settings-pane@0.8.9...@ali/lowcode-plugin-settings-pane@0.8.10) (2020-04-16)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-settings-pane
|
||||
|
||||
<a name="0.8.9"></a>
|
||||
## [0.8.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-settings-pane@0.8.8...@ali/lowcode-plugin-settings-pane@0.8.9) (2020-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* plugin-designer ([2dfbcd4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/2dfbcd4))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.8"></a>
|
||||
## [0.8.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-settings-pane@0.8.7...@ali/lowcode-plugin-settings-pane@0.8.8) (2020-03-31)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-settings-pane
|
||||
|
||||
<a name="0.8.7"></a>
|
||||
## [0.8.7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-settings-pane@0.8.6...@ali/lowcode-plugin-settings-pane@0.8.7) (2020-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **settings-pane:** overflow problem ([d2d8556](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/d2d8556))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.6"></a>
|
||||
## [0.8.6](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-settings-pane@0.8.5...@ali/lowcode-plugin-settings-pane@0.8.6) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-settings-pane
|
||||
|
||||
<a name="0.8.5"></a>
|
||||
## [0.8.5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-settings-pane@0.8.4...@ali/lowcode-plugin-settings-pane@0.8.5) (2020-03-30)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-settings-pane
|
||||
|
||||
<a name="0.8.4"></a>
|
||||
## 0.8.4 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add color-setter ([a149921](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a149921))
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7))
|
||||
* 增加color-setter,json-setter ([93e76ce](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/93e76ce))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.8.3"></a>
|
||||
## 0.8.3 (2020-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
<<<<<<< HEAD
|
||||
* add color-setter ([a149921](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a149921))
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7))
|
||||
* 增加color-setter,json-setter ([93e76ce](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/93e76ce))
|
||||
=======
|
||||
* add color-setter ([a149921](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a14992174b65b1241e7bb82561c7efdfd6589606))
|
||||
* double outline & ZH_EN support ([b379bd7](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b379bd7c0c488ef24f825760750a13d3fa083c96))
|
||||
* 增加color-setter,json-setter ([93e76ce](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/93e76ce3606603ee926ad83b21b29ffe28dc0682))
|
||||
>>>>>>> df955e1db90ff104cd11160def80113cfd6faccc
|
||||
@ -1,15 +0,0 @@
|
||||
属性面板
|
||||
|
||||
其中 field 可独立出去一个包
|
||||
|
||||
提供:
|
||||
1. 快捷设置面板服务
|
||||
2. 对应节点的设置面板服务
|
||||
3. 右侧设置面板
|
||||
4. 提供 setters 服务,setters 注册、获取机制
|
||||
|
||||
依赖:
|
||||
|
||||
tip 处理
|
||||
field
|
||||
popup
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"build-plugin-component",
|
||||
"build-plugin-fusion",
|
||||
["build-plugin-moment-locales", {
|
||||
"locales": ["zh-cn"]
|
||||
}]
|
||||
]
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "@ali/lowcode-plugin-settings-pane",
|
||||
"version": "0.8.10",
|
||||
"description": "Settings pane for Ali lowCode engine",
|
||||
"files": [
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"scripts": {
|
||||
"build": "build-scripts build --skip-demo",
|
||||
"test": "ava",
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^0.9.2",
|
||||
"@ali/lowcode-editor-core": "^0.8.5",
|
||||
"@ali/lowcode-globals": "^0.9.2",
|
||||
"@ali/lowcode-plugin-outline-pane": "^0.8.8",
|
||||
"@ali/ve-stage-box": "^4.0.0",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/react": "^16",
|
||||
"build-plugin-component": "^0.2.10",
|
||||
"build-plugin-fusion": "^0.1.1",
|
||||
"build-plugin-moment-locales": "^0.1.0"
|
||||
},
|
||||
"ava": {
|
||||
"compileEnhancements": false,
|
||||
"snapshotDir": "test/fixtures/__snapshots__",
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
]
|
||||
},
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npm.alibaba-inc.com"
|
||||
}
|
||||
}
|
||||
@ -1,184 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon } from '@alifd/next';
|
||||
import { Title, TitleContent } from '@ali/lowcode-globals';
|
||||
import { PopupPipe, PopupContext } from '../popup';
|
||||
import './index.less';
|
||||
|
||||
export interface FieldProps {
|
||||
className?: string;
|
||||
title?: TitleContent | null;
|
||||
defaultDisplay?: 'accordion' | 'inline' | 'block';
|
||||
collapsed?: boolean;
|
||||
onExpandChange?: (expandState: boolean) => void;
|
||||
}
|
||||
|
||||
export class Field extends Component<FieldProps> {
|
||||
state = {
|
||||
collapsed: this.props.collapsed,
|
||||
display: this.props.defaultDisplay || 'inline',
|
||||
};
|
||||
|
||||
private toggleExpand = () => {
|
||||
const { onExpandChange } = this.props;
|
||||
const collapsed = !this.state.collapsed;
|
||||
this.setState({
|
||||
collapsed,
|
||||
});
|
||||
onExpandChange && onExpandChange(!collapsed);
|
||||
};
|
||||
private body: HTMLDivElement | null = null;
|
||||
private dispose?: () => void;
|
||||
private deployBlockTesting() {
|
||||
if (this.dispose) {
|
||||
this.dispose();
|
||||
}
|
||||
const body = this.body;
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
const check = () => {
|
||||
const setter = body.firstElementChild;
|
||||
if (setter && setter.classList.contains('lc-block-setter')) {
|
||||
this.setState({
|
||||
display: 'block',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
display: 'inline',
|
||||
});
|
||||
}
|
||||
};
|
||||
const observer = new MutationObserver(check);
|
||||
check();
|
||||
observer.observe(body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
this.dispose = () => observer.disconnect();
|
||||
}
|
||||
componentDidMount() {
|
||||
const { defaultDisplay } = this.props;
|
||||
if (!defaultDisplay || defaultDisplay === 'inline') {
|
||||
this.deployBlockTesting();
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if (this.dispose) {
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, children, title } = this.props;
|
||||
const { display, collapsed } = this.state;
|
||||
const isAccordion = display === 'accordion';
|
||||
return (
|
||||
<div
|
||||
className={classNames(`lc-field lc-${display}-field`, className, {
|
||||
'lc-field-is-collapsed': isAccordion && collapsed,
|
||||
})}
|
||||
>
|
||||
<div className="lc-field-head" onClick={isAccordion ? this.toggleExpand : undefined}>
|
||||
<div className="lc-field-title">
|
||||
<Title title={title || ''} />
|
||||
</div>
|
||||
{isAccordion && <Icon className="lc-field-icon" type="arrow-up" size="xs" />}
|
||||
</div>
|
||||
<div key="body" ref={(shell) => (this.body = shell)} className="lc-field-body">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface PopupFieldProps extends FieldProps {
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export class PopupField extends Component<PopupFieldProps> {
|
||||
static contextType = PopupContext;
|
||||
private pipe: any;
|
||||
|
||||
static defaultProps: PopupFieldProps = {
|
||||
width: 300,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, children, title, width } = this.props;
|
||||
if (!this.pipe) {
|
||||
this.pipe = (this.context as PopupPipe).create({ width });
|
||||
}
|
||||
|
||||
const titleElement = title && (
|
||||
<div className="lc-field-title">
|
||||
<Title title={title} />
|
||||
</div>
|
||||
);
|
||||
|
||||
this.pipe.send(<div className="lc-field-body">{children}</div>, titleElement);
|
||||
|
||||
return (
|
||||
<div className={classNames('lc-field lc-popup-field', className)}>
|
||||
{title && (
|
||||
<div
|
||||
className="lc-field-head"
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target);
|
||||
}}
|
||||
>
|
||||
<div className="lc-field-title">
|
||||
<Title title={title} />
|
||||
</div>
|
||||
<Icon className="lc-field-icon" type="arrow-left" size="xs" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface EntryFieldProps extends FieldProps {
|
||||
stageName?: string;
|
||||
}
|
||||
|
||||
export class EntryField extends Component<EntryFieldProps> {
|
||||
render() {
|
||||
const { stageName, title, className } = this.props;
|
||||
const classNameList = classNames('engine-setting-field', 'engine-entry-field', className);
|
||||
const fieldProps: any = {};
|
||||
|
||||
if (stageName) {
|
||||
// 为 stage 切换奠定基础
|
||||
fieldProps['data-stage-target'] = stageName;
|
||||
}
|
||||
|
||||
const innerElements = [
|
||||
<span className="engine-field-title" key="field-title">
|
||||
{title}
|
||||
</span>,
|
||||
// renderTip(tip, { propName }),
|
||||
// <Icons name="arrow" className="engine-field-arrow" size="12px" key="engine-field-arrow-icon" />,
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={classNameList} {...fieldProps}>
|
||||
{innerElements}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class PlainField extends Component<FieldProps> {
|
||||
render() {
|
||||
const { className, children } = this.props;
|
||||
return (
|
||||
<div className={classNames(`lc-field lc-plain-field`, className)}>
|
||||
<div className="lc-field-body">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,154 +0,0 @@
|
||||
@import '../variables.less';
|
||||
|
||||
@x-gap: 10px;
|
||||
@y-gap: 8px;
|
||||
|
||||
.lc-field {
|
||||
// head
|
||||
.lc-field-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.lc-field-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.lc-field-icon {
|
||||
margin-right: @x-gap;
|
||||
transform-origin: center;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-plain-field {
|
||||
// for top-level style
|
||||
padding: 8px 10px;
|
||||
> .lc-field-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-inline-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// for top-level style
|
||||
padding: 8px 10px;
|
||||
|
||||
> .lc-field-head {
|
||||
width: 70px;
|
||||
margin-right: 1px;
|
||||
.lc-title-label {
|
||||
width: 70px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
> .lc-field-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-block-field, &.lc-accordion-field {
|
||||
display: block;
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid var(--color-line-normal, @dark-alpha-2);
|
||||
}
|
||||
> .lc-field-head {
|
||||
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, @dark-alpha-2);
|
||||
color: var(--color-title, @white-alpha-2);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
> .lc-field-body {
|
||||
padding: @y-gap @x-gap/2;
|
||||
}
|
||||
|
||||
+ .lc-inline-field {
|
||||
border-top: 1px solid var(--color-line-normal, @dark-alpha-2);
|
||||
}
|
||||
}
|
||||
|
||||
.lc-setter-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.lc-block-field {
|
||||
position: relative;
|
||||
>.lc-field-body>.lc-block-setter>.lc-setter-actions {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-accordion-field {
|
||||
// collapsed
|
||||
&.lc-field-is-collapsed {
|
||||
> .lc-field-head .lc-field-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
> .lc-field-body {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 邻近的保持上下距离
|
||||
+ .lc-field {
|
||||
margin-top: @y-gap;
|
||||
}
|
||||
}
|
||||
|
||||
// 2rd level reset
|
||||
.lc-field-body {
|
||||
.lc-inline-field {
|
||||
padding: @y-gap @x-gap/2 0 @x-gap/2;
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
+ .lc-accordion-field, +.lc-block-field {
|
||||
margin-top: @y-gap;
|
||||
}
|
||||
}
|
||||
|
||||
.lc-field {
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
.lc-accordion-field, .lc-block-field {
|
||||
> .lc-field-head {
|
||||
padding-left: @x-gap/2;
|
||||
background: var(--color-block-background-light);
|
||||
border-bottom-color: var(--color-line-light);
|
||||
> .lc-field-icon {
|
||||
margin-right: @x-gap/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3rd level field title width should short
|
||||
.lc-field-body .lc-inline-field {
|
||||
> .lc-field-head {
|
||||
width: 50px;
|
||||
.lc-title-label {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { ReactNode, createElement } from 'react';
|
||||
import { TitleContent } from '@ali/lowcode-globals';
|
||||
import './index.less';
|
||||
import { Field, PopupField, EntryField, PlainField } from './fields';
|
||||
|
||||
export interface FieldProps {
|
||||
className?: string;
|
||||
title?: TitleContent | null;
|
||||
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
|
||||
collapsed?: boolean;
|
||||
onExpandChange?: (collapsed: boolean) => void;
|
||||
[extra: string]: any;
|
||||
}
|
||||
|
||||
export function createField(props: FieldProps, children: ReactNode, type?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry') {
|
||||
if (type === 'popup') {
|
||||
return createElement(PopupField, props, children);
|
||||
}
|
||||
if (type === 'entry') {
|
||||
return createElement(EntryField, props, children);
|
||||
}
|
||||
if (type === 'plain' || !props.title) {
|
||||
return createElement(PlainField, props, children);
|
||||
}
|
||||
return createElement(Field, { ...props, defaultDisplay: type }, children);
|
||||
}
|
||||
|
||||
export { Field, PopupField, EntryField, PlainField };
|
||||
@ -1,16 +0,0 @@
|
||||
import { SVGIcon, IconProps } from "@ali/lowcode-globals";
|
||||
|
||||
export function IconConvert(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M508.16 889.6C291.84 889.6 115.2 714.24 115.2 497.92 115.2 281.6 291.84 106.24 509.44 106.24c43.52 0 85.76 6.4 124.16 20.48l-10.24 30.72c-35.84-11.52-72.96-17.92-113.92-17.92-199.68 0-362.24 161.28-362.24 359.68s162.56 358.4 360.96 358.4 359.68-161.28 359.68-359.68c0-66.56-17.92-131.84-51.2-185.6L844.8 294.4c37.12 60.16 56.32 130.56 56.32 203.52-1.28 216.32-176.64 391.68-392.96 391.68z" />
|
||||
<path d="M627.2 140.8m-15.36 0a15.36 15.36 0 1 0 30.72 0 15.36 15.36 0 1 0-30.72 0Z" />
|
||||
<path d="M832 304.64m-15.36 0a15.36 15.36 0 1 0 30.72 0 15.36 15.36 0 1 0-30.72 0Z" />
|
||||
<path d="M348.16 497.92m-35.84 0a35.84 35.84 0 1 0 71.68 0 35.84 35.84 0 1 0-71.68 0Z" fill="#35A2D4" />
|
||||
<path d="M508.16 497.92m-35.84 0a35.84 35.84 0 1 0 71.68 0 35.84 35.84 0 1 0-71.68 0Z" fill="#35A2D4" />
|
||||
<path d="M668.16 497.92m-35.84 0a35.84 35.84 0 1 0 71.68 0 35.84 35.84 0 1 0-71.68 0Z" fill="#35A2D4" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconConvert.displayName = 'Convert';
|
||||
@ -1,9 +0,0 @@
|
||||
import { createSettingFieldView } from './settings/settings-pane';
|
||||
import './transducers/register';
|
||||
import './setters/register';
|
||||
import './style.less';
|
||||
import SettingsMainView from './settings/main-view';
|
||||
|
||||
export default SettingsMainView;
|
||||
|
||||
export { createSettingFieldView };
|
||||
@ -1,150 +0,0 @@
|
||||
import { createContext, ReactNode, Component, PureComponent } from 'react';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Balloon } from '@alifd/next';
|
||||
import { uniqueId } from '@ali/lowcode-globals';
|
||||
import './style.less';
|
||||
|
||||
export const PopupContext = createContext<PopupPipe>({} as any);
|
||||
|
||||
export class PopupPipe {
|
||||
private emitter = new EventEmitter();
|
||||
private currentId?: string;
|
||||
|
||||
create(props?: object): { send: (content: ReactNode, title: ReactNode) => void; show: (target: Element) => void } {
|
||||
let sendContent: ReactNode = null;
|
||||
let sendTitle: ReactNode = null;
|
||||
const id = uniqueId('popup');
|
||||
return {
|
||||
send: (content: ReactNode, title: ReactNode) => {
|
||||
sendContent = content;
|
||||
sendTitle = title;
|
||||
if (this.currentId === id) {
|
||||
this.popup({
|
||||
...props,
|
||||
content,
|
||||
title,
|
||||
});
|
||||
}
|
||||
},
|
||||
show: (target: Element, actionKey?: string) => {
|
||||
this.currentId = id;
|
||||
this.popup(
|
||||
{
|
||||
...props,
|
||||
actionKey,
|
||||
content: sendContent,
|
||||
title: sendTitle,
|
||||
},
|
||||
target,
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private popup(props: object, target?: Element) {
|
||||
Promise.resolve().then(() => {
|
||||
this.emitter.emit('popupchange', props, target);
|
||||
});
|
||||
}
|
||||
|
||||
onPopupChange(fn: (props: object, target?: Element) => void): () => void {
|
||||
this.emitter.on('popupchange', fn);
|
||||
return () => {
|
||||
this.emitter.removeListener('popupchange', fn);
|
||||
};
|
||||
}
|
||||
|
||||
purge() {
|
||||
this.emitter.removeAllListeners();
|
||||
}
|
||||
}
|
||||
|
||||
export default class PopupService extends Component<{ actionKey?: string; safeId?: string }> {
|
||||
private popupPipe = new PopupPipe();
|
||||
|
||||
componentWillUnmount() {
|
||||
this.popupPipe.purge();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, actionKey, safeId } = this.props;
|
||||
return (
|
||||
<PopupContext.Provider value={this.popupPipe}>
|
||||
{children}
|
||||
<PopupContent key={'pop' + actionKey} safeId={safeId} />
|
||||
</PopupContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class PopupContent extends PureComponent<{ safeId?: string }> {
|
||||
static contextType = PopupContext;
|
||||
state: any = {
|
||||
visible: false,
|
||||
pos: {},
|
||||
};
|
||||
|
||||
private dispose = (this.context as PopupPipe).onPopupChange((props, target) => {
|
||||
const state: any = {
|
||||
...props,
|
||||
visible: true,
|
||||
};
|
||||
if (target) {
|
||||
const rect = target.getBoundingClientRect();
|
||||
state.pos = {
|
||||
top: rect.top,
|
||||
height: rect.height,
|
||||
};
|
||||
// todo: compute the align method
|
||||
}
|
||||
this.setState(state);
|
||||
});
|
||||
|
||||
componentWillUnmount() {
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { content, visible, width, title, pos, actionKey } = this.state;
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
let avoidLaterHidden = true;
|
||||
setTimeout(() => {
|
||||
avoidLaterHidden = false;
|
||||
}, 10);
|
||||
|
||||
const id = uniqueId('ball');
|
||||
|
||||
return (
|
||||
<Balloon
|
||||
className="lc-ballon"
|
||||
align="l"
|
||||
id={this.props.safeId}
|
||||
safeNode={id}
|
||||
visible={visible}
|
||||
style={{ width }}
|
||||
onVisibleChange={(visible) => {
|
||||
if (avoidLaterHidden) {
|
||||
return;
|
||||
}
|
||||
if (!visible) {
|
||||
this.setState({ visible: false });
|
||||
}
|
||||
}}
|
||||
trigger={<div className="lc-popup-placeholder" style={pos} />}
|
||||
triggerType="click"
|
||||
animation={false}
|
||||
// needAdjust
|
||||
shouldUpdatePosition
|
||||
>
|
||||
<div className="lc-ballon-title">{title}</div>
|
||||
<div className="lc-ballon-content">
|
||||
<PopupService actionKey={actionKey} safeId={id}>
|
||||
{content}
|
||||
</PopupService>
|
||||
</div>
|
||||
</Balloon>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
.lc-popup-placeholder {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.lc-ballon {
|
||||
padding: 10px;
|
||||
max-width: 640px;
|
||||
width: 640px;
|
||||
.lc-ballon-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
.lc-ballon-content {
|
||||
margin-top: 10px;
|
||||
// width: 300px;
|
||||
}
|
||||
.next-balloon-close {
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
* 拖拽排序有问题
|
||||
* forceInline 有问题
|
||||
* 部分改变不响应
|
||||
* 样式还原
|
||||
* autofocus
|
||||
@ -1,279 +0,0 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import { Icon, Button, Message } from '@alifd/next';
|
||||
import { Title, SetterType, FieldConfig, SetterConfig } from '@ali/lowcode-globals';
|
||||
import { createSettingFieldView } from '../../settings/settings-pane';
|
||||
import { PopupContext, PopupPipe } from '../../popup';
|
||||
import Sortable from './sortable';
|
||||
import './style.less';
|
||||
import { SettingField } from '@ali/lowcode-designer';
|
||||
|
||||
interface ArraySetterState {
|
||||
items: SettingField[];
|
||||
itemsMap: Map<string | number, SettingField>;
|
||||
prevLength: number;
|
||||
}
|
||||
|
||||
interface ArraySetterProps {
|
||||
value: any[];
|
||||
field: SettingField;
|
||||
itemSetter?: SetterType;
|
||||
columns?: FieldConfig[];
|
||||
multiValue?: boolean;
|
||||
}
|
||||
|
||||
export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
|
||||
static getDerivedStateFromProps(props: ArraySetterProps, state: ArraySetterState) {
|
||||
const { value, field } = props;
|
||||
const newLength = value && Array.isArray(value) ? value.length : 0;
|
||||
if (state && state.prevLength === newLength) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// props value length change will go here
|
||||
const originLength = state ? state.items.length : 0;
|
||||
if (state && originLength === newLength) {
|
||||
return {
|
||||
prevLength: newLength,
|
||||
};
|
||||
}
|
||||
|
||||
const itemsMap = state ? state.itemsMap : new Map<string | number, SettingField>();
|
||||
let items = state ? state.items.slice() : [];
|
||||
if (newLength > originLength) {
|
||||
for (let i = originLength; i < newLength; i++) {
|
||||
const item = field.createField({
|
||||
name: i,
|
||||
setter: props.itemSetter,
|
||||
// FIXME:
|
||||
forceInline: 1,
|
||||
});
|
||||
items[i] = item;
|
||||
itemsMap.set(item.id, item);
|
||||
}
|
||||
} else if (newLength < originLength) {
|
||||
const deletes = items.splice(newLength);
|
||||
deletes.forEach((item) => {
|
||||
itemsMap.delete(item.id);
|
||||
});
|
||||
}
|
||||
return {
|
||||
items,
|
||||
itemsMap,
|
||||
prevLength: newLength,
|
||||
};
|
||||
}
|
||||
|
||||
state: ArraySetterState = {
|
||||
items: [],
|
||||
itemsMap: new Map<string | number, SettingField>(),
|
||||
prevLength: 0,
|
||||
};
|
||||
|
||||
onSort(sortedIds: Array<string | number>) {
|
||||
const { itemsMap } = this.state;
|
||||
const items = sortedIds.map((id, index) => {
|
||||
const item = itemsMap.get(id)!;
|
||||
item.setKey(index);
|
||||
return item;
|
||||
});
|
||||
this.setState({
|
||||
items,
|
||||
});
|
||||
}
|
||||
|
||||
private scrollToLast: boolean = false;
|
||||
onAdd() {
|
||||
const { items, itemsMap } = this.state;
|
||||
const { itemSetter } = this.props;
|
||||
const initialValue = typeof itemSetter === 'object' ? (itemSetter as any).initialValue : null;
|
||||
const item = this.props.field.createField({
|
||||
name: items.length,
|
||||
setter: itemSetter,
|
||||
// FIXME:
|
||||
forceInline: 1,
|
||||
});
|
||||
items.push(item);
|
||||
itemsMap.set(item.id, item);
|
||||
item.setValue(typeof initialValue === 'function' ? initialValue(item) : initialValue);
|
||||
this.scrollToLast = true;
|
||||
this.setState({
|
||||
items: items.slice(),
|
||||
});
|
||||
}
|
||||
|
||||
onRemove(field: SettingField) {
|
||||
const { items } = this.state;
|
||||
let i = items.indexOf(field);
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
items.splice(i, 1);
|
||||
const l = items.length;
|
||||
while (i < l) {
|
||||
items[i].setKey(i);
|
||||
i++;
|
||||
}
|
||||
field.remove();
|
||||
this.setState({ items: items.slice() });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.state.items.forEach((field) => {
|
||||
field.purge();
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(_: any, nextState: ArraySetterState) {
|
||||
if (nextState.items !== this.state.items) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
let columns: any = null;
|
||||
if (this.props.columns) {
|
||||
columns = this.props.columns.map((column) => <Title key={column.name} title={column.title || (column.name as string)} />);
|
||||
}
|
||||
|
||||
const { items } = this.state;
|
||||
const scrollToLast = this.scrollToLast;
|
||||
this.scrollToLast = false;
|
||||
const lastIndex = items.length - 1;
|
||||
|
||||
const content =
|
||||
items.length > 0 ? (
|
||||
<div className="lc-setter-list-scroll-body">
|
||||
<Sortable itemClassName="lc-setter-list-card" onSort={this.onSort.bind(this)}>
|
||||
{items.map((field, index) => (
|
||||
<ArrayItem
|
||||
key={field.id}
|
||||
scrollIntoView={scrollToLast && index === lastIndex}
|
||||
field={field}
|
||||
onRemove={this.onRemove.bind(this, field)}
|
||||
/>
|
||||
))}
|
||||
</Sortable>
|
||||
</div>
|
||||
) : this.props.multiValue ? (
|
||||
<Message type="warning">当前选择了多个节点,且值不一致,修改会覆盖所有值</Message>
|
||||
) : (
|
||||
<Message type="notice">当前项目为空</Message>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="lc-setter-list lc-block-setter">
|
||||
{/*<div className="lc-block-setter-actions">
|
||||
<Button size="medium" onClick={this.onAdd.bind(this)}>
|
||||
<Icon type="add" />
|
||||
<span>添加</span>
|
||||
</Button>
|
||||
</div>*/}
|
||||
{columns && <div className="lc-setter-list-columns">{columns}</div>}
|
||||
{content}
|
||||
<Button className="lc-setter-list-add" type="primary" onClick={this.onAdd.bind(this)}>
|
||||
<Icon type="add" />
|
||||
<span>添加一项</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayItem extends Component<{
|
||||
field: SettingField;
|
||||
onRemove: () => void;
|
||||
scrollIntoView: boolean;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
private shell?: HTMLDivElement | null;
|
||||
componentDidMount() {
|
||||
if (this.props.scrollIntoView && this.shell) {
|
||||
this.shell.parentElement!.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { onRemove, field } = this.props;
|
||||
return (
|
||||
<div className="lc-listitem" ref={(ref) => (this.shell = ref)}>
|
||||
<div draggable className="lc-listitem-handler">
|
||||
<Icon type="ellipsis" size="small" />
|
||||
</div>
|
||||
<div className="lc-listitem-body">{createSettingFieldView(field, field.parent)}</div>
|
||||
<div className="lc-listitem-actions">
|
||||
<div className="lc-listitem-action" onClick={onRemove}>
|
||||
<Icon type="ashbin" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TableSetter extends ListSetter {
|
||||
// todo:
|
||||
// forceInline = 1
|
||||
// has more actions
|
||||
}
|
||||
|
||||
export default class ArraySetter extends Component<{
|
||||
value: any[];
|
||||
field: SettingField;
|
||||
itemSetter?: SetterType;
|
||||
mode?: 'popup' | 'list';
|
||||
forceInline?: boolean;
|
||||
multiValue?: boolean;
|
||||
}> {
|
||||
static contextType = PopupContext;
|
||||
private pipe: any;
|
||||
render() {
|
||||
const { mode, forceInline, ...props } = this.props;
|
||||
const { field, itemSetter } = props;
|
||||
let columns: FieldConfig[] | undefined;
|
||||
if ((itemSetter as SetterConfig)?.componentName === 'ObjectSetter') {
|
||||
const items: FieldConfig[] = (itemSetter as any).props?.config?.items;
|
||||
if (items && Array.isArray(items)) {
|
||||
columns = items.filter((item) => item.isRequired || item.important || (item.setter as any)?.isRequired);
|
||||
if (columns.length > 4) {
|
||||
columns = columns.slice(0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'popup' || forceInline) {
|
||||
const title = (
|
||||
<Fragment>
|
||||
编辑:
|
||||
<Title title={field.title} />
|
||||
</Fragment>
|
||||
);
|
||||
if (!this.pipe) {
|
||||
let width = 360;
|
||||
if (columns) {
|
||||
if (columns.length === 3) {
|
||||
width = 480;
|
||||
} else if (columns.length > 3) {
|
||||
width = 600;
|
||||
}
|
||||
}
|
||||
this.pipe = (this.context as PopupPipe).create({ width });
|
||||
}
|
||||
this.pipe.send(<TableSetter key={field.id} {...props} columns={columns} />, title);
|
||||
return (
|
||||
<Button
|
||||
type={forceInline ? 'normal' : 'primary'}
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target, field.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
{forceInline ? title : '编辑数组'}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return <ListSetter {...props} columns={columns?.slice(0, 2)} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
.lc-sortable {
|
||||
position: relative;
|
||||
|
||||
.lc-sortable-card {
|
||||
box-sizing: border-box;
|
||||
&:after, &:before {
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
&.lc-dragging {
|
||||
outline: 2px dashed var(--color-brand);
|
||||
outline-offset: -2px;
|
||||
> * {
|
||||
visibility: hidden;
|
||||
}
|
||||
border-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
[draggable] {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
}
|
||||
@ -1,220 +0,0 @@
|
||||
import { Component, Children, ReactElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './sortable.less';
|
||||
|
||||
class Sortable extends Component<{
|
||||
className?: string;
|
||||
itemClassName?: string;
|
||||
onSort?: (sortedIds: Array<string | number>) => void;
|
||||
dragImageSourceHandler?: (elem: Element) => Element;
|
||||
children: ReactElement[];
|
||||
}> {
|
||||
private shell?: HTMLDivElement | null;
|
||||
private items?: Array<string | number>;
|
||||
private willDetach?: () => void;
|
||||
componentDidMount() {
|
||||
const box = this.shell!;
|
||||
|
||||
let isDragEnd: boolean = false;
|
||||
|
||||
/**
|
||||
* target node to be dragged
|
||||
*/
|
||||
let source: Element | null;
|
||||
|
||||
/**
|
||||
* node to be placed
|
||||
*/
|
||||
let ref: Element | null;
|
||||
|
||||
/**
|
||||
* next sibling of the source node
|
||||
*/
|
||||
let origRef: Element | null;
|
||||
|
||||
/**
|
||||
* accurately locate the node from event
|
||||
*/
|
||||
const locate = (e: DragEvent) => {
|
||||
let y = e.clientY;
|
||||
if (e.view !== window && e.view!.frameElement) {
|
||||
y += e.view!.frameElement.getBoundingClientRect().top;
|
||||
}
|
||||
let node = box.firstElementChild as HTMLDivElement;
|
||||
while (node) {
|
||||
if (node !== source && node.dataset.id) {
|
||||
const rect = node.getBoundingClientRect();
|
||||
|
||||
if (rect.height <= 0) continue;
|
||||
if (y < rect.top + rect.height / 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node = node.nextElementSibling as HTMLDivElement;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* find the source node
|
||||
*/
|
||||
const getSource = (e: DragEvent) => {
|
||||
const target = e.target as Element;
|
||||
if (!target || !box.contains(target) || target === box) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let node = box.firstElementChild;
|
||||
while (node) {
|
||||
if (node.contains(target)) {
|
||||
return node;
|
||||
}
|
||||
node = node.nextElementSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const sort = (beforeId: string | number | null | undefined) => {
|
||||
if (!source) return;
|
||||
|
||||
const sourceId = (source as HTMLDivElement).dataset.id;
|
||||
const items = this.items!;
|
||||
const origIndex = items.findIndex(id => id == sourceId);
|
||||
|
||||
let newIndex = beforeId ? items.findIndex(id => id == beforeId) : items.length;
|
||||
|
||||
if (origIndex < 0 || newIndex < 0) return;
|
||||
if (this.props.onSort) {
|
||||
if (newIndex > origIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
if (origIndex === newIndex) return;
|
||||
const item = items.splice(origIndex, 1);
|
||||
items.splice(newIndex, 0, item[0]);
|
||||
|
||||
this.props.onSort(items);
|
||||
}
|
||||
};
|
||||
|
||||
const dragstart = (e: DragEvent) => {
|
||||
isDragEnd = false;
|
||||
source = getSource(e);
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
origRef = source.nextElementSibling;
|
||||
const rect = source.getBoundingClientRect();
|
||||
let dragSource = source;
|
||||
if (this.props.dragImageSourceHandler) {
|
||||
dragSource = this.props.dragImageSourceHandler(source);
|
||||
}
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.setDragImage(dragSource, e.clientX - rect.left, e.clientY - rect.top);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
try {
|
||||
e.dataTransfer.setData('application/json', {} as any);
|
||||
} catch (ex) {
|
||||
// eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
source!.classList.add('lc-dragging');
|
||||
}, 0);
|
||||
return true;
|
||||
};
|
||||
|
||||
const placeAt = (beforeRef: Element | null) => {
|
||||
if (beforeRef) {
|
||||
if (beforeRef !== source) {
|
||||
box.insertBefore(source!, beforeRef);
|
||||
}
|
||||
} else {
|
||||
box.appendChild(source!);
|
||||
}
|
||||
};
|
||||
|
||||
const adjust = (e: DragEvent) => {
|
||||
if (isDragEnd) return;
|
||||
ref = locate(e);
|
||||
placeAt(ref);
|
||||
};
|
||||
|
||||
let lastDragEvent: DragEvent | null;
|
||||
const drag = (e: DragEvent) => {
|
||||
if (!source) return;
|
||||
e.preventDefault();
|
||||
if (lastDragEvent) {
|
||||
if (lastDragEvent.clientX === e.clientX && lastDragEvent.clientY === e.clientY) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
lastDragEvent = e;
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
}
|
||||
adjust(e);
|
||||
};
|
||||
|
||||
const dragend = (e: DragEvent) => {
|
||||
isDragEnd = true;
|
||||
if (!source) return;
|
||||
e.preventDefault();
|
||||
source.classList.remove('lc-dragging');
|
||||
placeAt(origRef);
|
||||
sort(ref ? (ref as HTMLDivElement).dataset.id : null);
|
||||
source = null;
|
||||
ref = null;
|
||||
origRef = null;
|
||||
lastDragEvent = null;
|
||||
};
|
||||
|
||||
box.addEventListener('dragstart', dragstart);
|
||||
document.addEventListener('dragover', drag);
|
||||
document.addEventListener('drag', drag);
|
||||
document.addEventListener('dragend', dragend);
|
||||
|
||||
this.willDetach = () => {
|
||||
box.removeEventListener('dragstart', dragstart);
|
||||
document.removeEventListener('dragover', drag);
|
||||
document.removeEventListener('drag', drag);
|
||||
document.removeEventListener('dragend', dragend);
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.willDetach) {
|
||||
this.willDetach();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, itemClassName, children } = this.props;
|
||||
const items: Array<string | number> = [];
|
||||
const cards = Children.map(children, child => {
|
||||
const id = child.key!;
|
||||
items.push(id);
|
||||
return (
|
||||
<div key={id} data-id={id} className={classNames('lc-sortable-card', itemClassName)}>
|
||||
{child}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
this.items = items;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('lc-sortable', className)}
|
||||
ref={ref => {
|
||||
this.shell = ref;
|
||||
}}
|
||||
>
|
||||
{cards}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Sortable;
|
||||
@ -1,103 +0,0 @@
|
||||
.lc-setter-list {
|
||||
[draggable] {
|
||||
cursor: move;
|
||||
}
|
||||
color: var(--color-text);
|
||||
|
||||
.next-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
line-height: 1 !important;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.lc-setter-list-add {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 8px;;
|
||||
}
|
||||
|
||||
|
||||
.lc-setter-list-columns {
|
||||
display: flex;
|
||||
> .lc-title {
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
margin-left: 47px;
|
||||
margin-right: 28px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.lc-setter-list-scroll-body {
|
||||
margin: -8px -5px;
|
||||
padding: 8px 10px;
|
||||
overflow-y: auto;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.lc-setter-list-card {
|
||||
border: 1px solid rgba(31,56,88,.2);
|
||||
background-color: var(--color-block-background-light);
|
||||
border-radius: 3px;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.lc-listitem {
|
||||
position: relative;
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 34px;
|
||||
|
||||
.lc-listitem-actions {
|
||||
margin: 0 3px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
.lc-listitem-action {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.lc-listitem-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
text-overflow: ellipsis;
|
||||
.lc-field {
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
>.lc-field-body {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
> * {
|
||||
width: 100%;
|
||||
}
|
||||
.next-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.lc-listitem-handler {
|
||||
margin-left: 2px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
.next-icon-ellipsis {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,257 +0,0 @@
|
||||
import React, { Component, isValidElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Dropdown, Button, Menu } from '@alifd/next';
|
||||
import {
|
||||
getSetter,
|
||||
getSettersMap,
|
||||
SetterConfig,
|
||||
computed,
|
||||
obx,
|
||||
CustomView,
|
||||
DynamicProps,
|
||||
DynamicSetter,
|
||||
TitleContent,
|
||||
isSetterConfig,
|
||||
Title,
|
||||
createSetterContent,
|
||||
observer,
|
||||
isDynamicSetter,
|
||||
shallowIntl,
|
||||
EmbedTip,
|
||||
isI18nData,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { IconConvert } from '../../icons/convert';
|
||||
|
||||
import './style.less';
|
||||
import { SettingField } from '@ali/lowcode-designer';
|
||||
|
||||
export interface SetterItem {
|
||||
name: string;
|
||||
title: TitleContent;
|
||||
setter: string | DynamicSetter | CustomView;
|
||||
props?: object | DynamicProps;
|
||||
condition?: (field: SettingField) => boolean;
|
||||
initialValue?: any | ((field: SettingField) => any);
|
||||
list: boolean;
|
||||
}
|
||||
|
||||
function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | DynamicSetter>): SetterItem[] {
|
||||
if (!setters) {
|
||||
const normalized: SetterItem[] = [];
|
||||
getSettersMap().forEach((setter, name) => {
|
||||
if (name === 'MixedSetter') {
|
||||
return;
|
||||
}
|
||||
normalized.push({
|
||||
name,
|
||||
title: setter.title || name,
|
||||
setter: name,
|
||||
condition: setter.condition,
|
||||
initialValue: setter.initialValue,
|
||||
list: setter.recommend || false,
|
||||
});
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
const names: string[] = [];
|
||||
function generateName(n: string) {
|
||||
let idx = 1;
|
||||
let got = n;
|
||||
while (names.indexOf(got) > -1) {
|
||||
got = `${n}:${idx++}`;
|
||||
}
|
||||
names.push(got);
|
||||
return got;
|
||||
}
|
||||
return setters.map((setter) => {
|
||||
const config: any = {
|
||||
setter,
|
||||
list: true,
|
||||
};
|
||||
if (isSetterConfig(setter)) {
|
||||
config.setter = setter.componentName;
|
||||
config.props = setter.props;
|
||||
config.condition = setter.condition;
|
||||
config.initialValue = setter.initialValue;
|
||||
config.title = setter.title;
|
||||
}
|
||||
if (typeof config.setter === 'string') {
|
||||
config.name = config.setter;
|
||||
names.push(config.name);
|
||||
const info = getSetter(config.setter);
|
||||
if (!config.title) {
|
||||
config.title = info?.title || config.setter;
|
||||
}
|
||||
if (!config.condition) {
|
||||
config.condition = info?.condition;
|
||||
}
|
||||
if (!config.initialValue) {
|
||||
config.initialValue = info?.initialValue;
|
||||
}
|
||||
} else {
|
||||
config.name = generateName((config.setter as any).displayName || (config.setter as any).name || 'CustomSetter');
|
||||
if (!config.title) {
|
||||
config.title = config.name;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class MixedSetter extends Component<{
|
||||
field: SettingField;
|
||||
setters?: Array<string | SetterConfig | CustomView | DynamicSetter>;
|
||||
onSetterChange?: (field: SettingField, name: string) => void;
|
||||
onChange?: (val: any) => void;
|
||||
value?: any;
|
||||
className?: string;
|
||||
}> {
|
||||
private setters = nomalizeSetters(this.props.setters);
|
||||
@obx.ref private used?: string;
|
||||
@computed private getCurrentSetter() {
|
||||
const { field } = this.props;
|
||||
let firstMatched: SetterItem | undefined;
|
||||
for (const setter of this.setters) {
|
||||
const matched = !setter.condition || setter.condition(field);
|
||||
if (matched) {
|
||||
if (setter.name === this.used) {
|
||||
return setter;
|
||||
}
|
||||
if (!firstMatched) {
|
||||
firstMatched = setter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return firstMatched;
|
||||
}
|
||||
|
||||
private useSetter = (name: string) => {
|
||||
if (name === this.used) {
|
||||
return;
|
||||
}
|
||||
const { field, onChange } = this.props;
|
||||
const setter = this.setters.find((item) => item.name === name);
|
||||
this.used = name;
|
||||
if (setter) {
|
||||
let newValue: any = setter.initialValue;
|
||||
if (newValue && typeof newValue === 'function') {
|
||||
newValue = newValue(field);
|
||||
}
|
||||
onChange && onChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
private shell: HTMLDivElement | null = null;
|
||||
private checkIsBlockField() {
|
||||
if (this.shell) {
|
||||
const setter = this.shell.firstElementChild;
|
||||
if (setter && setter.classList.contains('lc-block-setter')) {
|
||||
this.shell.classList.add('lc-block-setter');
|
||||
} else {
|
||||
this.shell.classList.remove('lc-block-setter');
|
||||
}
|
||||
}
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
componentDidMount() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, field, setters, onSetterChange, ...restProps } = this.props;
|
||||
|
||||
const currentSetter = this.getCurrentSetter();
|
||||
const isTwoType = this.setters.length < 3;
|
||||
|
||||
let setterContent: any;
|
||||
const triggerTitle: any = {
|
||||
tip: {
|
||||
type: 'i18n',
|
||||
'zh-CN': '切换格式',
|
||||
'en-US': 'Switch Format',
|
||||
},
|
||||
icon: <IconConvert size={24} />,
|
||||
};
|
||||
if (currentSetter) {
|
||||
const { setter, title, props } = currentSetter;
|
||||
let setterProps: any = {};
|
||||
let setterType: any;
|
||||
if (isDynamicSetter(setter)) {
|
||||
setterType = setter.call(field, field);
|
||||
} else {
|
||||
setterType = setter;
|
||||
}
|
||||
if (props) {
|
||||
setterProps = props;
|
||||
if (typeof setterProps === 'function') {
|
||||
setterProps = setterProps(field);
|
||||
}
|
||||
}
|
||||
|
||||
setterContent = createSetterContent(setterType, {
|
||||
...shallowIntl(setterProps),
|
||||
field,
|
||||
...restProps,
|
||||
});
|
||||
if (title) {
|
||||
if (typeof title !== 'object' || isI18nData(title) || isValidElement(title)) {
|
||||
triggerTitle.tip = title;
|
||||
} else {
|
||||
triggerTitle.tip = title.tip || title.label;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 未匹配的 null 值,显示 NullValue 空值
|
||||
// 未匹配的 其它 值,显示 InvalidValue 非法值
|
||||
if (restProps.value == null) {
|
||||
setterContent = <span>NullValue</span>;
|
||||
} else {
|
||||
setterContent = <span>InvalidValue</span>;
|
||||
}
|
||||
}
|
||||
const usedName = currentSetter?.name || this.used;
|
||||
let moreBtnNode = (
|
||||
<Title
|
||||
title={triggerTitle}
|
||||
className="lc-switch-trigger"
|
||||
onClick={
|
||||
isTwoType
|
||||
? () => {
|
||||
if (this.setters[0]?.name === usedName) {
|
||||
this.useSetter(this.setters[1]?.name);
|
||||
} else {
|
||||
this.useSetter(this.setters[0]?.name);
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
if (!isTwoType) {
|
||||
moreBtnNode = (
|
||||
<Dropdown trigger={moreBtnNode} triggerType="click" align="tr br">
|
||||
<Menu selectMode="single" hasSelectedIcon={true} selectedKeys={usedName} onItemClick={this.useSetter}>
|
||||
{this.setters.filter(setter => setter.list || setter.name === usedName).map((setter) => {
|
||||
return (
|
||||
<Menu.Item key={setter.name}>
|
||||
<Title title={setter.title} />
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={(shell) => (this.shell = shell)} className={classNames('lc-setter-mixed', className)}>
|
||||
{setterContent}
|
||||
|
||||
<div className="lc-setter-actions">{moreBtnNode}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
.lc-setter-mixed {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-right: 26px;
|
||||
display: block;
|
||||
position: relative;
|
||||
>.lc-setter-actions {
|
||||
position: absolute;
|
||||
right: -2px;
|
||||
top: 50%;
|
||||
transform: translate(100%, -50%);
|
||||
.lc-switch-trigger {
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.next-input,.next-date-picker {
|
||||
width: 100%;
|
||||
}
|
||||
&.lc-block-setter {
|
||||
position: static;
|
||||
margin-right: 0;
|
||||
>.lc-setter-actions {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,180 +0,0 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import { Icon, Button } from '@alifd/next';
|
||||
import { Title, SetterType, FieldConfig } from '@ali/lowcode-globals';
|
||||
import { createSettingFieldView } from '../../settings/settings-pane';
|
||||
import { PopupContext, PopupPipe } from '../../popup';
|
||||
import { SettingField } from '@ali/lowcode-designer';
|
||||
import './style.less';
|
||||
|
||||
export default class ObjectSetter extends Component<{
|
||||
field: SettingField;
|
||||
descriptor?: string | ((rowField: SettingField) => string);
|
||||
config: ObjectSetterConfig;
|
||||
mode?: 'popup' | 'form';
|
||||
// 1: in tablerow 2: in listrow 3: in column-cell
|
||||
forceInline?: number;
|
||||
}> {
|
||||
render() {
|
||||
const { mode, forceInline = 0, ...props } = this.props;
|
||||
if (forceInline || mode === 'popup') {
|
||||
if (forceInline > 2 || mode === 'popup') {
|
||||
// popup
|
||||
return <RowSetter {...props} primaryButton={forceInline ? false : true} />;
|
||||
} else {
|
||||
return <RowSetter columns={forceInline > 1 ? 2 : 4} {...props} />;
|
||||
}
|
||||
} else {
|
||||
// form
|
||||
return <FormSetter {...props} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ObjectSetterConfig {
|
||||
items?: FieldConfig[];
|
||||
extraSetter?: SetterType;
|
||||
}
|
||||
|
||||
interface RowSetterProps {
|
||||
field: SettingField;
|
||||
descriptor?: string | ((rowField: SettingField) => string);
|
||||
config: ObjectSetterConfig;
|
||||
columns?: number;
|
||||
primaryButton?: boolean;
|
||||
}
|
||||
|
||||
class RowSetter extends Component<RowSetterProps> {
|
||||
static contextType = PopupContext;
|
||||
|
||||
state: any = {
|
||||
descriptor: '',
|
||||
};
|
||||
|
||||
private items?: SettingField[];
|
||||
constructor(props: RowSetterProps) {
|
||||
super(props);
|
||||
const { config, descriptor, field, columns } = props;
|
||||
const items: SettingField[] = [];
|
||||
if (columns && config.items) {
|
||||
const l = Math.min(config.items.length, columns);
|
||||
for (let i = 0; i < l; i++) {
|
||||
const conf = config.items[i];
|
||||
if (conf.isRequired || conf.important || (conf.setter as any)?.isRequired) {
|
||||
const item = field.createField({
|
||||
...conf,
|
||||
// in column-cell
|
||||
forceInline: 3,
|
||||
});
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.length > 0) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
let firstRun: boolean = true;
|
||||
field.onEffect(() => {
|
||||
let state: any = {};
|
||||
if (descriptor) {
|
||||
if (typeof descriptor === 'function') {
|
||||
state.descriptor = descriptor(field);
|
||||
} else {
|
||||
state.descriptor = field.getPropValue(descriptor);
|
||||
}
|
||||
} else {
|
||||
state.descriptor = field.title;
|
||||
}
|
||||
|
||||
if (firstRun) {
|
||||
firstRun = false;
|
||||
this.state = state;
|
||||
} else {
|
||||
this.setState(state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(_: any, nextState: any) {
|
||||
if (this.state.decriptor !== nextState.decriptor) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private pipe: any;
|
||||
render() {
|
||||
const items = this.items;
|
||||
const { field, primaryButton, config } = this.props;
|
||||
|
||||
if (!this.pipe) {
|
||||
this.pipe = (this.context as PopupPipe).create({ width: 320 });
|
||||
}
|
||||
|
||||
const title = (
|
||||
<Fragment>
|
||||
编辑:
|
||||
<Title title={this.state.descriptor} />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
this.pipe.send(<FormSetter key={field.id} field={field} config={config} />, title);
|
||||
|
||||
if (items) {
|
||||
return (
|
||||
<div className="lc-setter-object-row">
|
||||
<div
|
||||
className="lc-setter-object-row-edit"
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target, field.id);
|
||||
}}
|
||||
>
|
||||
<Icon size="small" type="edit" />
|
||||
</div>
|
||||
<div className="lc-setter-object-row-body">{items.map((item) => createSettingFieldView(item, field))}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
type={primaryButton === false ? 'normal' : 'primary'}
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target, field.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface FormSetterProps {
|
||||
field: SettingField;
|
||||
config: ObjectSetterConfig;
|
||||
}
|
||||
class FormSetter extends Component<FormSetterProps> {
|
||||
private items: SettingField[];
|
||||
constructor(props: RowSetterProps) {
|
||||
super(props);
|
||||
const { config, field } = props;
|
||||
this.items = (config.items || []).map((conf) => field.createField(conf));
|
||||
|
||||
// TODO: extraConfig for custom fields
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { field } = this.props;
|
||||
return (
|
||||
<div className="lc-setter-object lc-block-setter">
|
||||
{this.items.map((item, index) => createSettingFieldView(item, field, index))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
.lc-setter-object-row {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
.lc-setter-object-row-edit {
|
||||
width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.lc-setter-object-row-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
.lc-field {
|
||||
padding: 0 !important;
|
||||
.lc-field-body {
|
||||
padding: 0 !important; margin: 0 !important;
|
||||
}
|
||||
}
|
||||
> * {
|
||||
flex: 1;
|
||||
flex-shrink: 1;
|
||||
margin-left: 2px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
import { registerSetter, isPlainObject } from '@ali/lowcode-globals';
|
||||
import ArraySetter from './array-setter';
|
||||
import ObjectSetter from './object-setter';
|
||||
import MixedSetter from './mixed-setter';
|
||||
|
||||
registerSetter('ArraySetter', {
|
||||
component: ArraySetter,
|
||||
defaultProps: {},
|
||||
title: 'ArraySetter', // TODO
|
||||
condition: (field: any) => {
|
||||
const v = field.getValue();
|
||||
return v == null || Array.isArray(v);
|
||||
},
|
||||
initialValue: [],
|
||||
recommend: true,
|
||||
});
|
||||
registerSetter('ObjectSetter', {
|
||||
component: ObjectSetter,
|
||||
// todo: defaultProps
|
||||
defaultProps: {},
|
||||
title: 'ObjectSetter', // TODO
|
||||
condition: (field: any) => {
|
||||
const v = field.getValue();
|
||||
return v == null || isPlainObject(v);
|
||||
},
|
||||
initialValue: {},
|
||||
recommend: true,
|
||||
});
|
||||
registerSetter('MixedSetter', MixedSetter);
|
||||
@ -1,148 +0,0 @@
|
||||
import React, { Component, PureComponent } from 'react';
|
||||
import { Tab, Breadcrumb } from '@alifd/next';
|
||||
import { Title, createIcon, observer } from '@ali/lowcode-globals';
|
||||
import { Node, isSettingField, SettingField } from '@ali/lowcode-designer';
|
||||
import { Pane as OutlinePane } from '@ali/lowcode-plugin-outline-pane';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import { SettingsMain } from './main';
|
||||
import SettingsPane from './settings-pane';
|
||||
|
||||
@observer
|
||||
export default class SettingsMainView extends Component<{ editor: Editor }> {
|
||||
private main = new SettingsMain(this.props.editor);
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.main.purge();
|
||||
}
|
||||
|
||||
renderBreadcrumb() {
|
||||
const { settings } = this.main;
|
||||
if (!settings) {
|
||||
return null;
|
||||
}
|
||||
if (settings.isMultiple) {
|
||||
return (
|
||||
<div className="lc-settings-navigator">
|
||||
{createIcon(settings.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})}
|
||||
<Title title={settings.componentMeta!.title} />
|
||||
<span>x {settings.nodes.length}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let node: Node | null = settings.first;
|
||||
const items = [];
|
||||
let l = 3;
|
||||
while (l-- > 0 && node) {
|
||||
const props =
|
||||
l === 2
|
||||
? {}
|
||||
: {
|
||||
onMouseOver: hoverNode.bind(null, node, true),
|
||||
onMouseOut: hoverNode.bind(null, node, false),
|
||||
onClick: selectNode.bind(null, node),
|
||||
};
|
||||
items.unshift(<Breadcrumb.Item {...props} key={node.id}><Title title={node.title} /></Breadcrumb.Item>);
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lc-settings-navigator">
|
||||
{createIcon(this.main.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})}
|
||||
<Breadcrumb className="lc-settings-node-breadcrumb">{items}</Breadcrumb>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { settings } = this.main;
|
||||
if (!settings) {
|
||||
// 未选中节点,提示选中 或者 显示根节点设置
|
||||
return (
|
||||
<div className="lc-settings-main">
|
||||
<OutlinePaneEntry main={this.main} />
|
||||
<div className="lc-settings-notice">
|
||||
<p>请在左侧画布选中节点</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!settings.isSameComponent) {
|
||||
// todo: future support 获取设置项交集编辑
|
||||
return (
|
||||
<div className="lc-settings-main">
|
||||
<OutlinePaneEntry main={this.main} />
|
||||
<div className="lc-settings-notice">
|
||||
<p>请选中同一类型节点编辑</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { items } = settings;
|
||||
if (items.length > 5 || items.some(item => !isSettingField(item) || !item.isGroup)) {
|
||||
return (
|
||||
<div className="lc-settings-main">
|
||||
<OutlinePaneEntry main={this.main} />
|
||||
{this.renderBreadcrumb()}
|
||||
<div className="lc-settings-body">
|
||||
<SettingsPane target={settings} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lc-settings-main">
|
||||
<OutlinePaneEntry main={this.main} />
|
||||
<Tab
|
||||
navClassName="lc-settings-tabs"
|
||||
animation={false}
|
||||
excessMode="dropdown"
|
||||
contentClassName="lc-settings-tabs-content"
|
||||
extra={this.renderBreadcrumb()}
|
||||
>
|
||||
{(items as SettingField[]).map(field => (
|
||||
<Tab.Item className="lc-settings-tab-item" title={<Title title={field.title} />} key={field.name}>
|
||||
<SettingsPane target={field} key={field.id} />
|
||||
</Tab.Item>
|
||||
))}
|
||||
</Tab>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OutlinePaneEntry extends PureComponent<{ main: SettingsMain }> {
|
||||
state = {
|
||||
outlineInited: false,
|
||||
};
|
||||
private dispose = this.props.main.onceOutlineVisible(() => {
|
||||
this.setState({
|
||||
outlineInited: true,
|
||||
});
|
||||
});
|
||||
componentWillUnmount() {
|
||||
this.dispose();
|
||||
}
|
||||
render() {
|
||||
if (!this.state.outlineInited) {
|
||||
return null;
|
||||
}
|
||||
return <OutlinePane editor={this.props.main.editor} config={{
|
||||
name: '__IN_SETTINGS__'
|
||||
}} />;
|
||||
}
|
||||
}
|
||||
|
||||
function hoverNode(node: Node, flag: boolean) {
|
||||
node.hover(flag);
|
||||
}
|
||||
function selectNode(node: Node) {
|
||||
node.select();
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { obx, computed } from '@ali/lowcode-globals';
|
||||
import { Node, Designer, Selection, SettingTopEntry } from '@ali/lowcode-designer';
|
||||
import { getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
|
||||
function generateSessionId(nodes: Node[]) {
|
||||
return nodes
|
||||
.map((node) => node.id)
|
||||
.sort()
|
||||
.join(',');
|
||||
}
|
||||
|
||||
export class SettingsMain {
|
||||
private emitter = new EventEmitter();
|
||||
private _sessionId = '';
|
||||
@obx.ref private _settings?: SettingTopEntry;
|
||||
|
||||
@computed get length(): number | undefined {
|
||||
return this._settings?.nodes.length;
|
||||
}
|
||||
|
||||
@computed get componentMeta() {
|
||||
return this._settings?.componentMeta;
|
||||
}
|
||||
|
||||
get settings() {
|
||||
return this._settings;
|
||||
}
|
||||
|
||||
private disposeListener: () => void;
|
||||
|
||||
private designer?: Designer;
|
||||
|
||||
constructor(readonly editor: Editor) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private async init() {
|
||||
const setupSelection = (selection?: Selection) => {
|
||||
if (selection) {
|
||||
this.setup(selection.getNodes());
|
||||
} else {
|
||||
this.setup([]);
|
||||
}
|
||||
};
|
||||
this.editor.on('designer.selection.change', setupSelection);
|
||||
this.disposeListener = () => {
|
||||
this.editor.removeListener('designer.selection.change', setupSelection);
|
||||
};
|
||||
const designer = await this.editor.onceGot(Designer);
|
||||
this.designer = designer;
|
||||
getTreeMaster(designer).onceEnableBuiltin(() => {
|
||||
this.emitter.emit('outline-visible');
|
||||
});
|
||||
setupSelection(designer.currentSelection);
|
||||
}
|
||||
|
||||
private setup(nodes: Node[]) {
|
||||
// check nodes change
|
||||
const sessionId = generateSessionId(nodes);
|
||||
if (sessionId === this._sessionId) {
|
||||
return;
|
||||
}
|
||||
this._sessionId = sessionId;
|
||||
if (nodes.length < 1) {
|
||||
this._settings = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.designer) {
|
||||
this.designer = nodes[0].document.designer;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -1,149 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
import {
|
||||
createContent,
|
||||
CustomView,
|
||||
intl,
|
||||
shallowIntl,
|
||||
isSetterConfig,
|
||||
createSetterContent,
|
||||
observer,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { Field, createField } from '../field';
|
||||
import PopupService from '../popup';
|
||||
import { SettingField, isSettingField, SettingTopEntry, SettingEntry } from '@ali/lowcode-designer';
|
||||
|
||||
@observer
|
||||
class SettingFieldView extends Component<{ field: SettingField }> {
|
||||
render() {
|
||||
const { field } = this.props;
|
||||
const { extraProps } = field;
|
||||
const { condition, defaultValue } = extraProps;
|
||||
const visible = field.isSingle && typeof condition === 'function' ? condition(field) !== false : true;
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
const { setter } = field;
|
||||
|
||||
let setterProps: any = {};
|
||||
let setterType: any;
|
||||
if (Array.isArray(setter)) {
|
||||
setterType = 'MixedSetter';
|
||||
setterProps = {
|
||||
setters: setter,
|
||||
};
|
||||
} else if (isSetterConfig(setter)) {
|
||||
setterType = setter.componentName;
|
||||
if (setter.props) {
|
||||
setterProps = setter.props;
|
||||
if (typeof setterProps === 'function') {
|
||||
setterProps = setterProps(field);
|
||||
}
|
||||
}
|
||||
} else if (setter) {
|
||||
setterType = setter;
|
||||
}
|
||||
let value = null;
|
||||
if (field.type === 'field') {
|
||||
if (defaultValue != null && !('defaultValue' in setterProps)) {
|
||||
setterProps.defaultValue = defaultValue;
|
||||
}
|
||||
if (field.valueState > 0) {
|
||||
value = field.getValue();
|
||||
} else {
|
||||
setterProps.multiValue = true;
|
||||
if (!('placeholder' in setterProps)) {
|
||||
// FIXME! move to locale file
|
||||
setterProps.placeholder = intl({
|
||||
type: 'i18n',
|
||||
'zh-CN': '多种值',
|
||||
'en-US': 'Multiple Value',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: error handling
|
||||
|
||||
return createField({
|
||||
title: field.title,
|
||||
collapsed: !field.expanded,
|
||||
onExpandChange: (expandState) => field.setExpanded(expandState),
|
||||
}, createSetterContent(setterType, {
|
||||
...shallowIntl(setterProps),
|
||||
forceInline: extraProps.forceInline,
|
||||
key: field.id,
|
||||
// === injection
|
||||
prop: field, // for compatible vision
|
||||
field,
|
||||
// === IO
|
||||
value, // reaction point
|
||||
onChange: (value: any) => {
|
||||
this.setState({
|
||||
value,
|
||||
});
|
||||
field.setValue(value);
|
||||
},
|
||||
}), extraProps.forceInline ? 'plain' : extraProps.display);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class SettingGroupView extends Component<{ field: SettingField }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { field } = this.props;
|
||||
const { extraProps } = field;
|
||||
const { condition } = extraProps;
|
||||
const visible = field.isSingle && typeof condition === 'function' ? condition(field) !== false : true;
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// todo: split collapsed state | field.items for optimize
|
||||
return (
|
||||
<Field defaultDisplay="accordion" title={field.title} collapsed={!field.expanded} onExpandChange={(expandState) => {
|
||||
field.setExpanded(expandState);
|
||||
}}>
|
||||
{field.items.map((item, index) => createSettingFieldView(item, field, index))}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function createSettingFieldView(item: SettingField | CustomView, field: SettingEntry, index?: number) {
|
||||
if (isSettingField(item)) {
|
||||
if (item.isGroup) {
|
||||
return <SettingGroupView field={item} key={item.id} />;
|
||||
} else {
|
||||
return <SettingFieldView field={item} key={item.id} />;
|
||||
}
|
||||
} else {
|
||||
return createContent(item, { key: index, field });
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class SettingsPane extends Component<{ target: SettingTopEntry | SettingField }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { target } = this.props;
|
||||
const items = target.items
|
||||
return (
|
||||
<div className="lc-settings-pane">
|
||||
{/* todo: add head for single use */}
|
||||
<PopupService>
|
||||
<div className="lc-settings-content">
|
||||
{items.map((item, index) => createSettingFieldView(item, target, index))}
|
||||
</div>
|
||||
</PopupService>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
.lc-settings-main {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.lc-settings-notice {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-family: PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica,Arial,sans-serif;
|
||||
color: var(--color-text ,rgba(0,0,0,.6));
|
||||
padding: 50px 15px 0;
|
||||
}
|
||||
|
||||
.lc-settings-navigator {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 5px;
|
||||
border-bottom: 1px solid var(--color-line-normal);
|
||||
.lc-settings-navigator-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
* {
|
||||
fill: var(--color-icon-normal, rgba(31, 56, 88, 0.4));
|
||||
}
|
||||
}
|
||||
.lc-settings-node-breadcrumb {
|
||||
margin-left: 5px;
|
||||
.next-breadcrumb {
|
||||
display: inline-flex;
|
||||
align-items: stretch;
|
||||
height: 24px;
|
||||
}
|
||||
.next-breadcrumb-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
&:not(:last-child):hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.next-breadcrumb-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lc-settings-body {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
// ====== reset fusion-tabs =====
|
||||
.lc-settings-tabs {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
> .next-tabs-nav-extra {
|
||||
position: absolute !important;
|
||||
top: 40px !important;
|
||||
left: 0 !important;
|
||||
height: 30px;
|
||||
right: 0;
|
||||
transform: none !important;
|
||||
|
||||
}
|
||||
.next-tabs-nav-container {
|
||||
.next-tabs-nav {
|
||||
display: flex;
|
||||
.next-tabs-tab.lc-settings-tab-item {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
outline: none;
|
||||
.next-tabs-tab-inner {
|
||||
text-align: center;
|
||||
padding: 12px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lc-settings-tabs-content {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
left:0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
.next-tabs-tabpane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow-y: auto;
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
.lc-outline-pane {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
background-color: white;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lc-settings-pane {
|
||||
padding-bottom: 50px;
|
||||
.next-btn {
|
||||
line-height: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
html.lc-cursor-dragging:not(.lowcode-has-fixed-tree) {
|
||||
.lc-settings-main .lc-outline-pane {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@ -1,256 +0,0 @@
|
||||
import { TransformedComponentMetadata, FieldConfig } from '@ali/lowcode-globals';
|
||||
import { SettingField } from '../settings/setting-field';
|
||||
|
||||
export default function(metadata: TransformedComponentMetadata): TransformedComponentMetadata {
|
||||
const { componentName, configure = {} } = metadata;
|
||||
if (componentName === 'Leaf') {
|
||||
return {
|
||||
...metadata,
|
||||
configure: {
|
||||
...configure,
|
||||
combined: [
|
||||
{
|
||||
name: 'children',
|
||||
title: { type: 'i18n', 'zh-CN': '内容设置', 'en-US': 'Content' },
|
||||
setter: {
|
||||
componentName: 'MixinSetter',
|
||||
props: {
|
||||
// TODO:
|
||||
setters: [
|
||||
{
|
||||
componentName: 'StringSetter',
|
||||
props: {
|
||||
// TODO: textarea mode
|
||||
multiline: true,
|
||||
},
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
componentName: 'ExpressionSetter',
|
||||
initialValue: {
|
||||
type: 'JSExpression',
|
||||
value: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const { props, events = {}, styles } = configure as any;
|
||||
const isRoot: boolean = componentName === 'Page' || componentName === 'Component';
|
||||
const eventsDefinition: any[] = [];
|
||||
const supportedLifecycles =
|
||||
events.supportedLifecycles ||
|
||||
(isRoot
|
||||
? [
|
||||
{
|
||||
description: '初始化时',
|
||||
name: 'constructor',
|
||||
},
|
||||
{
|
||||
description: '装载后',
|
||||
name: 'componentDidMount',
|
||||
},
|
||||
{
|
||||
description: '更新时',
|
||||
name: 'componentDidMount',
|
||||
},
|
||||
{
|
||||
description: '卸载时',
|
||||
name: 'componentWillUnmount',
|
||||
},
|
||||
]
|
||||
: null);
|
||||
if (supportedLifecycles) {
|
||||
eventsDefinition.push({
|
||||
type: 'lifeCycleEvent',
|
||||
title: '生命周期',
|
||||
list: supportedLifecycles.map((event: any) => (typeof event === 'string' ? { name: event } : event)),
|
||||
});
|
||||
}
|
||||
if (events.supportedEvents) {
|
||||
eventsDefinition.push({
|
||||
type: 'events',
|
||||
title: '事件',
|
||||
list: (events.supportedEvents || []).map((event: any) => (typeof event === 'string' ? { name: event } : event)),
|
||||
});
|
||||
}
|
||||
// 通用设置
|
||||
const propsGroup = props || [];
|
||||
propsGroup.push({
|
||||
name: '#generals',
|
||||
title: { type: 'i18n', 'zh-CN': '通用', 'en-US': 'General' },
|
||||
items: [
|
||||
{
|
||||
name: 'id',
|
||||
title: 'ID',
|
||||
setter: 'StringSetter',
|
||||
},
|
||||
{
|
||||
name: 'key',
|
||||
title: 'Key',
|
||||
// todo: use Mixin
|
||||
setter: 'StringSetter',
|
||||
},
|
||||
{
|
||||
name: 'ref',
|
||||
title: 'Ref',
|
||||
setter: 'StringSetter',
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: '!more',
|
||||
title: '更多',
|
||||
setter: 'PropertiesSetter',
|
||||
},*/
|
||||
],
|
||||
});
|
||||
const combined: FieldConfig[] = [
|
||||
{
|
||||
title: { type: 'i18n', 'zh-CN': '属性', 'en-US': 'Props' },
|
||||
name: '#props',
|
||||
items: propsGroup,
|
||||
},
|
||||
];
|
||||
const stylesGroup: FieldConfig[] = [];
|
||||
if (styles?.supportClassName) {
|
||||
stylesGroup.push({
|
||||
name: 'className',
|
||||
title: { type: 'i18n', 'zh-CN': '类名绑定', 'en-US': 'ClassName' },
|
||||
setter: 'ClassNameSetter',
|
||||
});
|
||||
}
|
||||
if (styles?.supportInlineStyle) {
|
||||
stylesGroup.push({
|
||||
name: 'style',
|
||||
title: { type: 'i18n', 'zh-CN': '行内样式', 'en-US': 'Style' },
|
||||
setter: 'StyleSetter',
|
||||
});
|
||||
}
|
||||
if (stylesGroup.length > 0) {
|
||||
combined.push({
|
||||
name: '#styles',
|
||||
title: { type: 'i18n', 'zh-CN': '样式', 'en-US': 'Styles' },
|
||||
items: stylesGroup,
|
||||
});
|
||||
}
|
||||
|
||||
if (eventsDefinition.length > 0) {
|
||||
combined.push({
|
||||
name: '#events',
|
||||
title: { type: 'i18n', 'zh-CN': '事件', 'en-US': 'Events' },
|
||||
items: [
|
||||
{
|
||||
name: '!events',
|
||||
title: { type: 'i18n', 'zh-CN': '事件设置', 'en-US': 'Events' },
|
||||
setter: {
|
||||
componentName: 'EventsSetter',
|
||||
props: {
|
||||
definition: eventsDefinition,
|
||||
},
|
||||
},
|
||||
getValue(field: SettingField, val?: any[]) {
|
||||
// todo:
|
||||
return val;
|
||||
},
|
||||
|
||||
setValue(field: SettingField, eventDataList: any[]) {
|
||||
// todo:
|
||||
return;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (isRoot) {
|
||||
/*
|
||||
combined.push({
|
||||
name: '#advanced',
|
||||
title: { type: 'i18n', 'zh-CN': '高级', 'en-US': 'Advance' },
|
||||
items: [],
|
||||
});
|
||||
*/
|
||||
} else {
|
||||
combined.push({
|
||||
name: '#advanced',
|
||||
title: { type: 'i18n', 'zh-CN': '高级', 'en-US': 'Advance' },
|
||||
items: [
|
||||
{
|
||||
name: '__condition',
|
||||
title: { type: 'i18n', 'zh-CN': '条件显示', 'en-US': 'Condition' },
|
||||
setter: 'ExpressionSetter',
|
||||
},
|
||||
{
|
||||
name: '#loop',
|
||||
title: { type: 'i18n', 'zh-CN': '循环', 'en-US': 'Loop' },
|
||||
items: [
|
||||
{
|
||||
name: '__loop',
|
||||
title: { type: 'i18n', 'zh-CN': '循环数据', 'en-US': 'Loop Data' },
|
||||
setter: {
|
||||
componentName: 'MixinSetter',
|
||||
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' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '__loopArgs.0',
|
||||
title: { type: 'i18n', 'zh-CN': '迭代变量名', 'en-US': 'Loop Item' },
|
||||
setter: {
|
||||
componentName: 'StringSetter',
|
||||
props: {
|
||||
placeholder: { type: 'i18n', 'zh-CN': '默认为: item', 'en-US': 'Defaults: item' },
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '__loopArgs.1',
|
||||
title: { type: 'i18n', 'zh-CN': '索引变量名', 'en-US': 'Loop Index' },
|
||||
setter: {
|
||||
componentName: 'StringSetter',
|
||||
props: {
|
||||
placeholder: { type: 'i18n', 'zh-CN': '默认为: index', 'en-US': 'Defaults: index' },
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'key',
|
||||
title: 'Key',
|
||||
setter: 'ExpressionSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
configure: {
|
||||
...configure,
|
||||
combined,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,232 +0,0 @@
|
||||
import {
|
||||
FieldConfig,
|
||||
PropConfig,
|
||||
PropType,
|
||||
SetterType,
|
||||
OneOf,
|
||||
Shape,
|
||||
ObjectOf,
|
||||
ArrayOf,
|
||||
TransformedComponentMetadata,
|
||||
} from '@ali/lowcode-globals';
|
||||
|
||||
function propConfigToFieldConfig(propConfig: PropConfig): FieldConfig {
|
||||
const { name, description } = propConfig;
|
||||
const title = {
|
||||
label: {
|
||||
type: 'i18n',
|
||||
'en-US': name,
|
||||
'zh-CN': description?.slice(0, 10) || name,
|
||||
},
|
||||
tip: description ? `${name} | ${description}` : undefined,
|
||||
};
|
||||
return {
|
||||
title,
|
||||
...propConfig,
|
||||
setter: propTypeToSetter(propConfig.propType),
|
||||
};
|
||||
}
|
||||
|
||||
function propTypeToSetter(propType: PropType): SetterType {
|
||||
let typeName: string;
|
||||
let isRequired: boolean | undefined = false;
|
||||
if (typeof propType === 'string') {
|
||||
typeName = propType;
|
||||
} else {
|
||||
typeName = propType.type;
|
||||
isRequired = propType.isRequired;
|
||||
}
|
||||
// TODO: use mixinSetter wrapper
|
||||
switch (typeName) {
|
||||
case 'string':
|
||||
return {
|
||||
componentName: 'StringSetter',
|
||||
isRequired,
|
||||
initialValue: '',
|
||||
};
|
||||
|
||||
case 'number':
|
||||
return {
|
||||
componentName: 'NumberSetter',
|
||||
isRequired,
|
||||
initialValue: 0,
|
||||
};
|
||||
case 'bool':
|
||||
return {
|
||||
componentName: 'NumberSetter',
|
||||
isRequired,
|
||||
initialValue: false,
|
||||
};
|
||||
case 'oneOf':
|
||||
const dataSource = ((propType as OneOf).value || []).map((value, index) => {
|
||||
const t = typeof value;
|
||||
return {
|
||||
label: t === 'string' || t === 'number' || t === 'boolean' ? String(value) : `value ${index}`,
|
||||
value,
|
||||
};
|
||||
});
|
||||
const componentName = dataSource.length > 4 ? 'SelectSetter' : 'RadioGroupSetter';
|
||||
return {
|
||||
componentName,
|
||||
props: { dataSource },
|
||||
isRequired,
|
||||
initialValue: dataSource[0] ? dataSource[0].value : null,
|
||||
};
|
||||
|
||||
case 'element':
|
||||
case 'node': // TODO: use Mixin
|
||||
return {
|
||||
// slotSetter
|
||||
componentName: 'NodeSetter',
|
||||
props: {
|
||||
mode: typeName,
|
||||
},
|
||||
isRequired,
|
||||
initialValue: {
|
||||
type: 'JSSlot',
|
||||
value: '',
|
||||
},
|
||||
};
|
||||
case 'shape':
|
||||
case 'exact':
|
||||
const items = (propType as Shape).value.map((item) => propConfigToFieldConfig(item));
|
||||
return {
|
||||
componentName: 'ObjectSetter',
|
||||
props: {
|
||||
config: {
|
||||
items,
|
||||
extraSetter: typeName === 'shape' ? propTypeToSetter('any') : null,
|
||||
},
|
||||
},
|
||||
isRequired,
|
||||
initialValue: (field: any) => {
|
||||
const data: any = {};
|
||||
items.forEach((item) => {
|
||||
let initial = item.defaultValue;
|
||||
if (initial == null && item.setter && typeof item.setter === 'object') {
|
||||
initial = (item.setter as any).initialValue;
|
||||
}
|
||||
data[item.name] = initial ? (typeof initial === 'function' ? initial(field) : initial) : null;
|
||||
});
|
||||
return data;
|
||||
},
|
||||
};
|
||||
case 'object':
|
||||
case 'objectOf':
|
||||
return {
|
||||
componentName: 'ObjectSetter',
|
||||
props: {
|
||||
config: {
|
||||
extraSetter: propTypeToSetter(typeName === 'objectOf' ? (propType as ObjectOf).value : 'any'),
|
||||
},
|
||||
},
|
||||
isRequired,
|
||||
};
|
||||
case 'array':
|
||||
case 'arrayOf':
|
||||
return {
|
||||
componentName: 'ArraySetter',
|
||||
props: {
|
||||
itemSetter: propTypeToSetter(typeName === 'arrayOf' ? (propType as ArrayOf).value : 'any'),
|
||||
},
|
||||
isRequired,
|
||||
initialValue: [],
|
||||
};
|
||||
case 'func':
|
||||
return {
|
||||
componentName: 'FunctionSetter',
|
||||
isRequired,
|
||||
initialValue: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(){}',
|
||||
},
|
||||
};
|
||||
case 'oneOfType':
|
||||
return {
|
||||
componentName: 'MixinSetter',
|
||||
props: {
|
||||
// TODO:
|
||||
// setters: (propType as OneOfType).value.map(item => propTypeToSetter(item)),
|
||||
},
|
||||
isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
componentName: 'MixinSetter',
|
||||
isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
const EVENT_RE = /^on[A-Z][\w]*$/;
|
||||
|
||||
export default function(metadata: TransformedComponentMetadata): TransformedComponentMetadata {
|
||||
const { configure } = metadata;
|
||||
if (configure.props) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
if (!metadata.props) {
|
||||
return {
|
||||
...metadata,
|
||||
configure: {
|
||||
...configure,
|
||||
props: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
const { component = {}, events = {}, styles = {} } = configure;
|
||||
const supportedEvents: any[] | null = (events as any).supportedEvents ? null : [];
|
||||
const props: FieldConfig[] = [];
|
||||
|
||||
metadata.props.forEach((prop) => {
|
||||
const { name, propType, description } = prop;
|
||||
if (
|
||||
name === 'children' &&
|
||||
(component.isContainer || propType === 'node' || propType === 'element' || propType === 'any')
|
||||
) {
|
||||
if (component.isContainer !== false) {
|
||||
component.isContainer = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (EVENT_RE.test(name) && (propType === 'func' || propType === 'any')) {
|
||||
if (supportedEvents) {
|
||||
supportedEvents.push({
|
||||
name,
|
||||
description,
|
||||
});
|
||||
(events as any).supportedEvents = supportedEvents;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === 'className' && (propType === 'string' || propType === 'any')) {
|
||||
if ((styles as any).supportClassName == null) {
|
||||
(styles as any).supportClassName = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === 'style' && (propType === 'object' || propType === 'any')) {
|
||||
if ((styles as any).supportInlineStyle == null) {
|
||||
(styles as any).supportInlineStyle = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
props.push(propConfigToFieldConfig(prop));
|
||||
});
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
configure: {
|
||||
...configure,
|
||||
props,
|
||||
events,
|
||||
styles,
|
||||
component,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import { registerMetadataTransducer } from '@ali/lowcode-globals';
|
||||
import parseProps from './parse-props';
|
||||
import addonCombine from './addon-combine';
|
||||
|
||||
// parseProps
|
||||
registerMetadataTransducer(parseProps, 10, 'parse-props');
|
||||
|
||||
// addon/platform custom
|
||||
registerMetadataTransducer(addonCombine, 11, 'combine-props');
|
||||
@ -1,41 +0,0 @@
|
||||
function getHotterFromSetter(setter) {
|
||||
return setter && (setter.Hotter || (setter.type && setter.type.Hotter)) || []; // eslint-disable-line
|
||||
}
|
||||
|
||||
function getTransducerFromSetter(setter) {
|
||||
return setter && (
|
||||
setter.transducer || setter.Transducer
|
||||
|| (setter.type && (setter.type.transducer || setter.type.Transducer))
|
||||
) || null; // eslint-disable-line
|
||||
}
|
||||
|
||||
function combineTransducer(transducer, arr, context) {
|
||||
if (!transducer && Array.isArray(arr)) {
|
||||
const [toHot, toNative] = arr;
|
||||
transducer = { toHot, toNative };
|
||||
}
|
||||
|
||||
return {
|
||||
toHot: (transducer && transducer.toHot || (x => x)).bind(context), // eslint-disable-line
|
||||
toNative: (transducer && transducer.toNative || (x => x)).bind(context), // eslint-disable-line
|
||||
};
|
||||
}
|
||||
|
||||
export class Transducer {
|
||||
constructor(context, config) {
|
||||
this.setterTransducer = combineTransducer(
|
||||
getTransducerFromSetter(config.setter),
|
||||
getHotterFromSetter(config.setter),
|
||||
context,
|
||||
);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
toHot(data) {
|
||||
return this.setterTransducer.toHot(data);
|
||||
}
|
||||
|
||||
toNative(data) {
|
||||
return this.setterTransducer.toNative(data);
|
||||
}
|
||||
}
|
||||
@ -1,170 +0,0 @@
|
||||
/*
|
||||
* 基础的 DPL 定义使用了 kuma base 的定义,参考:
|
||||
* https://github.com/uxcore/kuma-base/tree/master/variables
|
||||
*/
|
||||
|
||||
/**
|
||||
* ===========================================================
|
||||
* ==================== Font Family ==========================
|
||||
* ===========================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* @font-family: "STHeiti", "Microsoft Yahei", "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
|
||||
*/
|
||||
|
||||
@font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
|
||||
@font-family-code: Monaco, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
|
||||
|
||||
/**
|
||||
* ===========================================================
|
||||
* ===================== Color DPL ===========================
|
||||
* ===========================================================
|
||||
*/
|
||||
|
||||
@brand-color-1: rgba(0, 108, 255, 1);
|
||||
@brand-color-2: rgba(25, 122, 255, 1);
|
||||
@brand-color-3: rgba(0, 96, 229, 1);
|
||||
|
||||
@brand-color-1-3: rgba(0, 108, 255, 0.6);
|
||||
@brand-color-1-4: rgba(0, 108, 255, 0.4);
|
||||
@brand-color-1-5: rgba(0, 108, 255, 0.3);
|
||||
@brand-color-1-6: rgba(0, 108, 255, 0.2);
|
||||
@brand-color-1-7: rgba(0, 108, 255, 0.1);
|
||||
|
||||
@brand-color: @brand-color-1;
|
||||
|
||||
@white-alpha-1: rgb(255, 255, 255); // W-1
|
||||
@white-alpha-2: rgba(255, 255, 255, 0.8); // W-2 A80
|
||||
@white-alpha-3: rgba(255, 255, 255, 0.6); // W-3 A60
|
||||
@white-alpha-4: rgba(255, 255, 255, 0.4); // W-4 A40
|
||||
@white-alpha-5: rgba(255, 255, 255, 0.3); // W-5 A30
|
||||
@white-alpha-6: rgba(255, 255, 255, 0.2); // W-6 A20
|
||||
@white-alpha-7: rgba(255, 255, 255, 0.1); // W-7 A10
|
||||
@white-alpha-8: rgba(255, 255, 255, 0.06); // W-8 A6
|
||||
|
||||
@dark-alpha-1: rgba(0, 0, 0, 1); // D-1 A100
|
||||
@dark-alpha-2: rgba(0, 0, 0, 0.8); // D-2 A80
|
||||
@dark-alpha-3: rgba(0, 0, 0, 0.6); // D-3 A60
|
||||
@dark-alpha-4: rgba(0, 0, 0, 0.4); // D-4 A40
|
||||
@dark-alpha-5: rgba(0, 0, 0, 0.3); // D-5 A30
|
||||
@dark-alpha-6: rgba(0, 0, 0, 0.2); // D-6 A20
|
||||
@dark-alpha-7: rgba(0, 0, 0, 0.1); // D-7 A10
|
||||
@dark-alpha-8: rgba(0, 0, 0, 0.06); // D-8 A6
|
||||
@dark-alpha-9: rgba(0, 0, 0, 0.04); // D-9 A4
|
||||
|
||||
@normal-alpha-1: rgba(31, 56, 88, 1); // N-1 A100
|
||||
@normal-alpha-2: rgba(31, 56, 88, 0.8); // N-2 A80
|
||||
@normal-alpha-3: rgba(31, 56, 88, 0.6); // N-3 A60
|
||||
@normal-alpha-4: rgba(31, 56, 88, 0.4); // N-4 A40
|
||||
@normal-alpha-5: rgba(31, 56, 88, 0.3); // N-5 A30
|
||||
@normal-alpha-6: rgba(31, 56, 88, 0.2); // N-6 A20
|
||||
@normal-alpha-7: rgba(31, 56, 88, 0.1); // N-7 A10
|
||||
@normal-alpha-8: rgba(31, 56, 88, 0.06); // N-8 A6
|
||||
@normal-alpha-9: rgba(31, 56, 88, 0.04); // N-9 A4
|
||||
|
||||
@normal-3: #77879c;
|
||||
@normal-4: #a3aebd;
|
||||
@normal-5: #bac3cc;
|
||||
@normal-6: #d1d7de;
|
||||
|
||||
@gray-dark: #333; // N2_4
|
||||
@gray: #666; // N2_3
|
||||
@gray-light: #999; // N2_2
|
||||
@gray-lighter: #ccc; // N2_1
|
||||
|
||||
@brand-secondary: #2c2f33; // B2_3
|
||||
// 补色
|
||||
@brand-complement: #00b3e8; // B3_1
|
||||
// 复合
|
||||
@brand-comosite: #00c587; // B3_2
|
||||
// 浓度
|
||||
@brand-deep: #73461d; // B3_3
|
||||
|
||||
// F1-1
|
||||
@brand-danger: rgb(240, 70, 49);
|
||||
// F1-2 (10% white)
|
||||
@brand-danger-hover: rgba(240, 70, 49, 0.9);
|
||||
// F1-3 (5% black)
|
||||
@brand-danger-focus: rgba(240, 70, 49, 0.95);
|
||||
|
||||
// F2-1
|
||||
@brand-warning: rgb(250, 189, 14);
|
||||
// F3-1
|
||||
@brand-success: rgb(102, 188, 92);
|
||||
// F4-1
|
||||
@brand-link: rgb(102, 188, 92);
|
||||
// F4-2
|
||||
@brand-link-hover: #2e76a6;
|
||||
|
||||
// F1-1-7 A10
|
||||
@brand-danger-alpha-7: rgba(240, 70, 49, 0.9);
|
||||
// F1-1-8 A6
|
||||
@brand-danger-alpha-8: rgba(240, 70, 49, 0.8);
|
||||
// F2-1-2 A80
|
||||
@brand-warning-alpha-2: rgba(250, 189, 14, 0.8);
|
||||
// F2-1-7 A10
|
||||
@brand-warning-alpha-7: rgba(250, 189, 14, 0.9);
|
||||
// F3-1-2 A80
|
||||
@brand-success-alpha-2: rgba(102, 188, 92, 0.8);
|
||||
// F3-1-7 A10
|
||||
@brand-success-alpha-7: rgba(102, 188, 92, 0.9);
|
||||
// F4-1-7 A10
|
||||
@brand-link-alpha-7: rgba(102, 188, 92, 0.9);
|
||||
|
||||
// 文本色
|
||||
@text-primary-color: @dark-alpha-3;
|
||||
@text-secondary-color: @normal-alpha-3;
|
||||
@text-thirdary-color: @dark-alpha-4;
|
||||
@text-disabled-color: @normal-alpha-5;
|
||||
@text-helper-color: @dark-alpha-4;
|
||||
@text-danger-color: @brand-danger;
|
||||
@text-ali-color: #ec6c00;
|
||||
|
||||
/**
|
||||
* ===========================================================
|
||||
* =================== Shadow Box ============================
|
||||
* ===========================================================
|
||||
*/
|
||||
|
||||
@box-shadow-1: 0 1px 4px 0 rgba(31, 56, 88, 0.15); // 1 级阴影,物体由原来存在于底面的物体展开,物体和底面关联紧密
|
||||
@box-shadow-2: 0 2px 10px 0 rgba(31, 56, 88, 0.15); // 2 级阴影,hover状态,物体层级较高
|
||||
@box-shadow-3: 0 4px 15px 0 rgba(31, 56, 88, 0.15); // 3 级阴影,当物体层级高于所有界面元素,弹窗用
|
||||
|
||||
/**
|
||||
* ===========================================================
|
||||
* ================= FontSize of Level =======================
|
||||
* ===========================================================
|
||||
*/
|
||||
|
||||
@fontSize-1: 26px;
|
||||
@fontSize-2: 20px;
|
||||
@fontSize-3: 16px;
|
||||
@fontSize-4: 14px;
|
||||
@fontSize-5: 12px;
|
||||
|
||||
@fontLineHeight-1: 38px;
|
||||
@fontLineHeight-2: 30px;
|
||||
@fontLineHeight-3: 26px;
|
||||
@fontLineHeight-4: 24px;
|
||||
@fontLineHeight-5: 20px;
|
||||
|
||||
/**
|
||||
* ===========================================================
|
||||
* ================= FontSize of Level =======================
|
||||
* ===========================================================
|
||||
*/
|
||||
|
||||
@global-border-radius: 3px;
|
||||
@input-border-radius: 3px;
|
||||
@popup-border-radius: 6px;
|
||||
|
||||
/**
|
||||
* ===========================================================
|
||||
* ===================== Transistion =========================
|
||||
* ===========================================================
|
||||
*/
|
||||
|
||||
@transition-duration: 0.3s;
|
||||
@transition-ease: cubic-bezier(0.23, 1, 0.32, 1);
|
||||
@transition-delay: 0s;
|
||||
@ -1,9 +1,8 @@
|
||||
import { Component, isValidElement, ReactElement, ReactNode } from 'react';
|
||||
import { Tab, Search, Input, Button } from '@alifd/next';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import { js_beautify, css_beautify } from 'js-beautify';
|
||||
import MonacoEditor from 'react-monaco-editor';
|
||||
import Panel from '../../vision-polyfill/src/skeleton/widget/panel';
|
||||
|
||||
// import lolizer from './sorceEditorPlugin',
|
||||
|
||||
@ -47,12 +46,12 @@ interface FunctionEventParam {
|
||||
|
||||
export default class SourceEditor extends Component<{
|
||||
editor: Editor;
|
||||
panel?: Panel
|
||||
panel?: any
|
||||
}> {
|
||||
private monocoEditer: Object;
|
||||
private editorCmd: Object;
|
||||
private monocoEditer: any;
|
||||
private editorCmd: any;
|
||||
|
||||
state = {
|
||||
state: any = {
|
||||
isShow: false,
|
||||
tabKey: TAB_KEY.JS_TAB,
|
||||
};
|
||||
@ -117,18 +116,13 @@ export default class SourceEditor extends Component<{
|
||||
}
|
||||
|
||||
openPluginPannel = () => {
|
||||
const { editor, panel } = this.props;
|
||||
// 判断面板是否处于激活状态
|
||||
if (!editor.leftNav || editor.leftNav != 'sourceEditor') {
|
||||
// 打开面板
|
||||
editor.emit('leftNav.change', 'sourceEditor');
|
||||
}
|
||||
const { panel } = this.props;
|
||||
if (panel) {
|
||||
panel.show();
|
||||
}
|
||||
}
|
||||
|
||||
callEditorEvent = (eventName, params) => {
|
||||
callEditorEvent = (eventName: any, params: any) => {
|
||||
if (!this.monocoEditer) {
|
||||
this.editorCmd = {
|
||||
eventName,
|
||||
@ -146,7 +140,7 @@ export default class SourceEditor extends Component<{
|
||||
|
||||
};
|
||||
|
||||
initCode = (schema) => {
|
||||
initCode = (schema: any) => {
|
||||
let jsCode = js_beautify(transfrom.schema2Code(schema), { indent_size: 2, indent_empty_lines: true });
|
||||
let css;
|
||||
|
||||
@ -167,13 +161,13 @@ export default class SourceEditor extends Component<{
|
||||
*/
|
||||
addFunction(params: FunctionEventParam) {
|
||||
const count = this.monocoEditer.getModel().getLineCount() || 0;
|
||||
const range = new monaco.Range(count, 1, count, 1);
|
||||
const range = new (window as any).monaco.Range(count, 1, count, 1);
|
||||
const functionCode = transfrom.getNewFunctionCode(params.functionName);
|
||||
this.monocoEditer.executeEdits('log-source', [
|
||||
{ identifier: 'event_id', range: range, text: functionCode, forceMoveMarkers: true },
|
||||
]);
|
||||
setTimeout(() => {
|
||||
let newPosition = new monaco.Position(count + 1, 2);
|
||||
let newPosition = new (window as any).monaco.Position(count + 1, 2);
|
||||
this.monocoEditer.setPosition(newPosition);
|
||||
this.monocoEditer.focus();
|
||||
}, 100);
|
||||
@ -204,7 +198,7 @@ export default class SourceEditor extends Component<{
|
||||
}
|
||||
}
|
||||
|
||||
editorDidMount = (editor, monaco) => {
|
||||
editorDidMount = (editor: any, monaco: any) => {
|
||||
console.log('editorDidMount', editor);
|
||||
this.monocoEditer = editor;
|
||||
|
||||
@ -247,13 +241,13 @@ export default class SourceEditor extends Component<{
|
||||
// });
|
||||
};
|
||||
|
||||
onTabChange = (key) => {
|
||||
onTabChange = (key: any) => {
|
||||
this.setState({
|
||||
selectTab: key,
|
||||
});
|
||||
};
|
||||
|
||||
updateCode = (newCode) => {
|
||||
updateCode = (newCode: any) => {
|
||||
const { selectTab } = this.state;
|
||||
if (selectTab === TAB_KEY.JS_TAB) {
|
||||
this.setState({
|
||||
@ -283,7 +277,7 @@ export default class SourceEditor extends Component<{
|
||||
{isShow && (
|
||||
<MonacoEditor
|
||||
value={selectTab == TAB_KEY.JS_TAB ? jsCode : css}
|
||||
{...defaultEditorOption}
|
||||
{...defaultEditorOption as any}
|
||||
{...{ language: selectTab == TAB_KEY.JS_TAB ? 'javascript' : 'css' }}
|
||||
onChange={(newCode) => this.updateCode(newCode)}
|
||||
editorDidMount={this.editorDidMount}
|
||||
|
||||
1
packages/plugin-source-editor/src/module.d.ts
vendored
Normal file
1
packages/plugin-source-editor/src/module.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module "js-beautify";
|
||||
@ -22,6 +22,8 @@
|
||||
"@ali/lowcode-designer": "^0.9.3",
|
||||
"@ali/lowcode-editor-core": "^0.8.6",
|
||||
"@ali/lowcode-editor-skeleton": "^0.8.7",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"@ali/lowcode-utils": "^0.8.0",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
},
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import './index.scss';
|
||||
import Editor, { PluginProps } from '@ali/lowcode-editor-core';
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import { TopIcon } from '@ali/lowcode-editor-skeleton';
|
||||
import { Designer } from '@ali/lowcode-designer';
|
||||
import { PluginProps } from '@ali/lowcode-types';
|
||||
|
||||
export interface IProps {
|
||||
export interface IProps extends PluginProps {
|
||||
editor: Editor;
|
||||
logo?: string;
|
||||
}
|
||||
@ -14,10 +15,7 @@ export interface IState {
|
||||
redoEnable: boolean;
|
||||
}
|
||||
|
||||
export default class UndoRedo extends PureComponent<
|
||||
IProps & PluginProps,
|
||||
IState
|
||||
> {
|
||||
export default class UndoRedo extends PureComponent<IProps, IState> {
|
||||
public static display = 'LowcodeUndoRedo';
|
||||
|
||||
private history: any;
|
||||
@ -80,18 +78,8 @@ export default class UndoRedo extends PureComponent<
|
||||
const { undoEnable, redoEnable } = this.state;
|
||||
return (
|
||||
<div className="lowcode-plugin-undo-redo">
|
||||
<TopIcon
|
||||
icon="houtui"
|
||||
title="后退"
|
||||
disabled={!undoEnable}
|
||||
onClick={this.handleUndoClick}
|
||||
/>
|
||||
<TopIcon
|
||||
icon="qianjin"
|
||||
title="前进"
|
||||
disabled={!redoEnable}
|
||||
onClick={this.handleRedoClick}
|
||||
/>
|
||||
<TopIcon icon="houtui" title="后退" disabled={!undoEnable} onClick={this.handleUndoClick} />
|
||||
<TopIcon icon="qianjin" title="前进" disabled={!redoEnable} onClick={this.handleRedoClick} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-editor-core": "^0.8.6",
|
||||
"@ali/lowcode-globals": "^0.9.3",
|
||||
"@ali/lowcode-utils": "^0.8.0",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-globals';
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconEn(props: IconProps) {
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-globals';
|
||||
import { SVGIcon, IconProps } from '@ali/lowcode-utils';
|
||||
|
||||
export function IconZh(props: IconProps) {
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createIntl } from '@ali/lowcode-globals';
|
||||
import { createIntl } from '@ali/lowcode-editor-core';
|
||||
import en_US from './en-US.json';
|
||||
import zh_CN from './zh-CN.json';
|
||||
|
||||
|
||||
@ -14,7 +14,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^0.9.3",
|
||||
"@ali/lowcode-globals": "^0.9.3",
|
||||
"@ali/lowcode-types": "^0.8.0",
|
||||
"@ali/lowcode-utils": "^0.8.0",
|
||||
"@ali/lowcode-react-renderer": "^0.8.4",
|
||||
"@recore/obx": "^1.0.8",
|
||||
"@recore/obx-react": "^1.0.7",
|
||||
|
||||
@ -30,7 +30,7 @@ export const DateYearSetter = DatePicker.YearPicker;
|
||||
export const DateMonthSetter = DatePicker.MonthPicker;
|
||||
export const DateRangeSetter = DatePicker.RangePicker;
|
||||
|
||||
export { ExpressionSetter, MixinSetter, EventsSetter }
|
||||
export { ExpressionSetter, EventsSetter }
|
||||
|
||||
// todo:
|
||||
export const ClassNameSetter = () => {
|
||||
|
||||
@ -14,21 +14,14 @@
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.19.16",
|
||||
"classnames": "^2.2.6",
|
||||
"monaco-editor": "^0.20.0",
|
||||
"react": "^16",
|
||||
"react-dom": "^16.7.0"
|
||||
"power-di": "^2.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/react": "^16",
|
||||
"@types/react-dom": "^16",
|
||||
"build-plugin-component": "^0.2.10",
|
||||
"build-plugin-fusion": "^0.1.0",
|
||||
"build-plugin-moment-locales": "^0.1.0"
|
||||
"build-plugin-component": "^0.2.10"
|
||||
},
|
||||
"ava": {
|
||||
"compileEnhancements": false,
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
import { isReactComponent } from '@ali/lowcode-utils';
|
||||
import { ComponentType, ReactElement, isValidElement } from 'react';
|
||||
import { ComponentClass, Component, ComponentType, ReactElement, isValidElement } from 'react';
|
||||
import { TitleContent } from './title';
|
||||
import { SettingTarget } from './setting-target';
|
||||
|
||||
function isReactClass(obj: any): obj is ComponentClass<any> {
|
||||
return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component);
|
||||
}
|
||||
|
||||
function isReactComponent(obj: any): obj is ComponentType<any> {
|
||||
return obj && (isReactClass(obj) || typeof obj === 'function');
|
||||
}
|
||||
|
||||
export type CustomView = ReactElement | ComponentType<any>;
|
||||
|
||||
export type DynamicProps = (target: SettingTarget) => object;
|
||||
|
||||
1
packages/utils/README.md
Normal file
1
packages/utils/README.md
Normal file
@ -0,0 +1 @@
|
||||
公用函数集合
|
||||
5
packages/utils/build.json
Normal file
5
packages/utils/build.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
"build-plugin-component"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@ali/lowcode-utils",
|
||||
"version": "0.8.0",
|
||||
"description": "Utils for Ali lowCode engine",
|
||||
"files": [
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"scripts": {
|
||||
"build": "build-scripts build --skip-demo",
|
||||
"test": "ava",
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^16",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"@ali/lowcode-utils": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/react": "^16",
|
||||
"build-plugin-component": "^0.2.10"
|
||||
},
|
||||
"ava": {
|
||||
"compileEnhancements": false,
|
||||
"snapshotDir": "test/fixtures/__snapshots__",
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npm.alibaba-inc.com"
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,11 @@
|
||||
import { ComponentType, ReactElement } from 'react';
|
||||
import { ComponentMetadata, FieldConfig, InitialItem } from '@ali/lowcode-types';
|
||||
import {
|
||||
ComponentMetadata,
|
||||
uniqueId,
|
||||
ComponentMeta,
|
||||
addBuiltinComponentAction,
|
||||
isComponentMeta,
|
||||
registerMetadataTransducer,
|
||||
FieldConfig,
|
||||
InitialItem,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { ComponentMeta, addBuiltinComponentAction, isComponentMeta } from '@ali/lowcode-designer';
|
||||
} from '@ali/lowcode-designer';
|
||||
import {
|
||||
OldPropConfig,
|
||||
OldPrototypeConfig,
|
||||
@ -16,6 +15,7 @@ import {
|
||||
upgradeConfigure,
|
||||
} from './upgrade-metadata';
|
||||
import { designer } from '../editor';
|
||||
import { uniqueId } from '@ali/lowcode-utils';
|
||||
|
||||
const GlobalPropsConfigure: Array<{ position: string; initials?: InitialItem[]; config: FieldConfig }> = [];
|
||||
const Overrides: {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ReactElement, ComponentType } from 'react';
|
||||
import { EventEmitter } from 'events';
|
||||
import { registerSetter } from '@ali/lowcode-globals';
|
||||
import { registerSetter } from '@ali/lowcode-editor-core';
|
||||
import Bundle from './bundle';
|
||||
|
||||
export class Trunk {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react';
|
||||
import { isI18nData, SettingTarget, InitialItem, isPlainObject, isJSSlot, isJSExpression } from '@ali/lowcode-globals';
|
||||
import { isPlainObject } from '@ali/lowcode-utils';
|
||||
import { isI18nData, SettingTarget, InitialItem, isJSSlot, isJSExpression } from '@ali/lowcode-types';
|
||||
|
||||
type Field = SettingTarget;
|
||||
|
||||
export enum DISPLAY_TYPE {
|
||||
NONE = 'none', // => condition'plain'
|
||||
NONE = 'none', // => condition'plain'
|
||||
PLAIN = 'plain',
|
||||
INLINE = 'inline',
|
||||
BLOCK = 'block',
|
||||
@ -35,7 +36,7 @@ export interface OldPropConfig {
|
||||
};
|
||||
defaultValue?: any; // => extraProps.defaultValue
|
||||
initialValue?: any | ((value: any, defaultValue: any) => any); // => initials.initialValue
|
||||
initial?: (value: any, defaultValue: any) => any // => initials.initialValue
|
||||
initial?: (value: any, defaultValue: any) => any; // => initials.initialValue
|
||||
|
||||
display?: DISPLAY_TYPE; // => fieldExtraProps
|
||||
fieldStyle?: DISPLAY_TYPE; // => fieldExtraProps
|
||||
@ -71,12 +72,12 @@ export interface OldPropConfig {
|
||||
mutator?( // => setValue
|
||||
this: Field,
|
||||
value: any,
|
||||
/*
|
||||
hotValue: any, // => x
|
||||
hotValue: any,
|
||||
): /*
|
||||
preValue: any, // => x
|
||||
preHotValue: any, // => x
|
||||
*/
|
||||
): void;
|
||||
void;
|
||||
/**
|
||||
* other values' change will trigger sync function here
|
||||
*/
|
||||
@ -218,13 +219,13 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
newConfig.title = {
|
||||
label: title,
|
||||
tip: tip.content,
|
||||
docUrl: tip.url
|
||||
docUrl: tip.url,
|
||||
};
|
||||
} else {
|
||||
newConfig.title = {
|
||||
...(title as any),
|
||||
tip: tip.content,
|
||||
docUrl: tip.url
|
||||
docUrl: tip.url,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -260,7 +261,9 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
if (ignore != null || disabled != null) {
|
||||
// FIXME! addFilter
|
||||
extraProps.virtual = (field: Field) => {
|
||||
if (isDisabled(field)) { return true; }
|
||||
if (isDisabled(field)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof ignore === 'function') {
|
||||
return ignore.call(field, field.getValue()) === true;
|
||||
@ -302,7 +305,7 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
}
|
||||
|
||||
return currentValue == null ? defaults : currentValue;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (sync) {
|
||||
@ -311,11 +314,11 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
if (value !== undefined) {
|
||||
field.setValue(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (mutator && !slotName) {
|
||||
extraProps.setValue = (field: Field, value: any) => {
|
||||
mutator.call(field, value);
|
||||
mutator.call(field, value, value);
|
||||
};
|
||||
}
|
||||
|
||||
@ -324,18 +327,20 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
if (!newConfig.title && slotTitle) {
|
||||
newConfig.title = slotTitle;
|
||||
}
|
||||
const setters: any[] = [{
|
||||
componentName: 'SlotSetter',
|
||||
initialValue: (field: any, value: any) => {
|
||||
if (isJSSlot(value)) {
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
type: 'JSSlot',
|
||||
value: value == null ? initialChildren : value
|
||||
};
|
||||
const setters: any[] = [
|
||||
{
|
||||
componentName: 'SlotSetter',
|
||||
initialValue: (field: any, value: any) => {
|
||||
if (isJSSlot(value)) {
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
type: 'JSSlot',
|
||||
value: value == null ? initialChildren : value,
|
||||
};
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
if (allowTextInput !== false) {
|
||||
setters.unshift('StringSetter');
|
||||
// FIXME: use I18nSetter
|
||||
@ -351,19 +356,21 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
let primarySetter: any;
|
||||
if (type === 'composite') {
|
||||
const initials: InitialItem[] = [];
|
||||
const objItems = items ? upgradeConfigure(items, (item) => {
|
||||
initials.push(item);
|
||||
}) : [];
|
||||
const objItems = items
|
||||
? upgradeConfigure(items, (item) => {
|
||||
initials.push(item);
|
||||
})
|
||||
: [];
|
||||
const initial = (target: SettingTarget, value?: any) => {
|
||||
// TODO:
|
||||
const defaults = extraProps.defaultValue;
|
||||
const data: any = {};
|
||||
initials.forEach(item => {
|
||||
initials.forEach((item) => {
|
||||
// FIXME! Target may be a wrong
|
||||
data[item.name] = item.initial(target, isPlainObject(value) ? value[item.name] : null);
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
addInitial({
|
||||
name,
|
||||
initial,
|
||||
@ -385,9 +392,11 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
primarySetter = setter.map(({ setter, condition }) => {
|
||||
return {
|
||||
componentName: setter,
|
||||
condition: condition ? (field: Field) => {
|
||||
return condition.call(field, field.getValue());
|
||||
} : null,
|
||||
condition: condition
|
||||
? (field: Field) => {
|
||||
return condition.call(field, field.getValue());
|
||||
}
|
||||
: null,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
@ -396,7 +405,9 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
}
|
||||
if (supportVariable) {
|
||||
if (primarySetter) {
|
||||
const setters = Array.isArray(primarySetter) ? primarySetter.concat('ExpressionSetter') : [primarySetter, 'ExpressionSetter'];
|
||||
const setters = Array.isArray(primarySetter)
|
||||
? primarySetter.concat('ExpressionSetter')
|
||||
: [primarySetter, 'ExpressionSetter'];
|
||||
primarySetter = {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
@ -405,8 +416,8 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
|
||||
if (useVariableChange) {
|
||||
useVariableChange.call(field, { isUseVariable: name === 'ExpressionSetter' });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
primarySetter = 'ExpressionSetter';
|
||||
@ -447,7 +458,7 @@ export function upgradeActions(actions?: Array<ComponentType<any> | ReactElement
|
||||
return actions.map((content) => {
|
||||
const type: any = isValidElement(content) ? content.type : content;
|
||||
if (typeof content === 'function') {
|
||||
const fn = content as (() => ReactElement);
|
||||
const fn = content as () => ReactElement;
|
||||
content = (({ node }: any) => {
|
||||
fn.call(node);
|
||||
}) as any;
|
||||
@ -457,7 +468,7 @@ export function upgradeActions(actions?: Array<ComponentType<any> | ReactElement
|
||||
content,
|
||||
important: true,
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -492,10 +503,11 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
canDroping,
|
||||
|
||||
// hooks
|
||||
canDraging, canDragging, // handleDragging
|
||||
canDraging,
|
||||
canDragging, // handleDragging
|
||||
// events
|
||||
didDropOut, // onNodeRemove
|
||||
didDropIn, // onNodeAdd
|
||||
didDropIn, // onNodeAdd
|
||||
subtreeModified, // onSubtreeModified
|
||||
|
||||
canResizing, // resizing
|
||||
@ -504,7 +516,6 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
onResizeEnd, // onResizeEnd
|
||||
} = oldConfig;
|
||||
|
||||
|
||||
const meta: any = {
|
||||
componentName,
|
||||
title,
|
||||
@ -566,7 +577,7 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
experimental.context = context;
|
||||
}
|
||||
if (snippets) {
|
||||
experimental.snippets = snippets.map(data => {
|
||||
experimental.snippets = snippets.map((data) => {
|
||||
const { schema = {} } = data;
|
||||
if (!schema.children && initialChildren && typeof initialChildren !== 'function') {
|
||||
schema.children = initialChildren;
|
||||
@ -596,9 +607,12 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
}
|
||||
}
|
||||
if (initialChildren) {
|
||||
experimental.initialChildren = typeof initialChildren === 'function' ? (field: Field) => {
|
||||
return initialChildren.call(field, (field as any).props);
|
||||
} : initialChildren;
|
||||
experimental.initialChildren =
|
||||
typeof initialChildren === 'function'
|
||||
? (field: Field) => {
|
||||
return initialChildren.call(field, (field as any).props);
|
||||
}
|
||||
: initialChildren;
|
||||
}
|
||||
if (view) {
|
||||
experimental.view = view;
|
||||
@ -644,21 +658,21 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
// todo: what is trigger?
|
||||
const { trigger, deltaX, deltaY } = e;
|
||||
onResize(e, trigger, currentNode, deltaX, deltaY);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (onResizeStart) {
|
||||
callbacks.onResizeStart = (e: any, currentNode: any) => {
|
||||
// todo: what is trigger?
|
||||
const { trigger } = e;
|
||||
onResizeStart(e, trigger, currentNode);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (onResizeEnd) {
|
||||
callbacks.onResizeEnd = (e: any, currentNode: any) => {
|
||||
// todo: what is trigger?
|
||||
const { trigger } = e;
|
||||
onResizeEnd(e, trigger, currentNode);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
experimental.callbacks = callbacks;
|
||||
@ -675,5 +689,3 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
meta.experimental = experimental;
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import getTrunkPane from '@ali/ve-trunk-pane';
|
||||
import EventBindDialog from '@ali/lowcode-plugin-event-bind-dialog';
|
||||
import loadUrls from './loader';
|
||||
import { upgradeAssetsBundle } from './upgrade-assets';
|
||||
import { isCSSUrl } from '@ali/lowcode-globals';
|
||||
import { isCSSUrl } from '@ali/lowcode-utils';
|
||||
|
||||
const { editor, skeleton } = Engine;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { designer } from './editor';
|
||||
import { DragObjectType, isNode, TransformStage } from '@ali/lowcode-designer';
|
||||
import { DragObjectType, isNode } from '@ali/lowcode-designer';
|
||||
|
||||
const dragon = designer.dragon;
|
||||
const DragEngine = {
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { globalContext, isPlainObject, isJSBlock } from '@ali/lowcode-globals';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import { isJSBlock } from '@ali/lowcode-types';
|
||||
import { isPlainObject } from '@ali/lowcode-utils';
|
||||
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||
import { Designer, TransformStage } from '@ali/lowcode-designer';
|
||||
import { registerSetters } from '@ali/lowcode-setters';
|
||||
import Outline from '@ali/lowcode-plugin-outline-pane';
|
||||
import SettingsPane from '@ali/lowcode-plugin-settings-pane';
|
||||
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
||||
import { Skeleton } from './skeleton/skeleton';
|
||||
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
|
||||
|
||||
|
||||
import Preview from '@ali/lowcode-plugin-sample-preview';
|
||||
@ -78,7 +78,7 @@ skeleton.add({
|
||||
area: 'rightArea',
|
||||
name: 'settingsPane',
|
||||
type: 'Panel',
|
||||
content: SettingsPane,
|
||||
content: SettingsPrimaryPane,
|
||||
});
|
||||
skeleton.add({
|
||||
area: 'leftArea',
|
||||
|
||||
3
packages/vision-polyfill/src/module.d.ts
vendored
Normal file
3
packages/vision-polyfill/src/module.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare module "@ali/visualengine";
|
||||
declare module "@ali/visualengine-utils";
|
||||
declare module "@ali/ve-trunk-pane"
|
||||
@ -1,5 +1,5 @@
|
||||
import { designer } from './editor';
|
||||
import { RootSchema } from '@ali/lowcode-globals';
|
||||
import { RootSchema } from '@ali/lowcode-types';
|
||||
import { DocumentModel } from '@ali/lowcode-designer';
|
||||
|
||||
const { project } = designer;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { skeleton, editor } from './editor';
|
||||
import { ReactElement } from 'react';
|
||||
import { IWidgetBaseConfig } from './skeleton/types';
|
||||
import { IWidgetBaseConfig } from '@ali/lowcode-editor-skeleton';
|
||||
|
||||
export interface IContentItemConfig {
|
||||
title: string;
|
||||
|
||||
@ -4,7 +4,7 @@ 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 I18nUtil from '@ali/ve-i18n-util';
|
||||
import { getSetter } from '@ali/lowcode-globals';
|
||||
import { getSetter } from '@ali/lowcode-editor-core';
|
||||
import { editor } from './editor';
|
||||
import { OldPropConfig, DISPLAY_TYPE } from './bundle/upgrade-metadata';
|
||||
|
||||
@ -264,7 +264,7 @@ export default class Prop implements IVariableSettable {
|
||||
}) {
|
||||
const accessor = this.config.accessor;
|
||||
if (accessor && (!options || !options.disableAccessor)) {
|
||||
const value = accessor.call(this, this.value);
|
||||
const value = accessor.call(this as any, this.value);
|
||||
if (!disableCache) {
|
||||
this.value = value;
|
||||
}
|
||||
@ -314,7 +314,7 @@ export default class Prop implements IVariableSettable {
|
||||
|
||||
const sync = this.config.sync;
|
||||
if (sync) {
|
||||
const value = sync.call(this, this.getValue(true));
|
||||
const value = sync.call(this as any, this.getValue(true));
|
||||
if (value !== undefined) {
|
||||
this.setValue(value);
|
||||
}
|
||||
@ -377,7 +377,7 @@ export default class Prop implements IVariableSettable {
|
||||
|
||||
this.emitter.emit('ve.prop.useVariableChange', { isUseVariable: flag });
|
||||
if (this.config.useVariableChange) {
|
||||
this.config.useVariableChange.call(this, { isUseVariable: flag });
|
||||
this.config.useVariableChange.call(this as any, { isUseVariable: flag });
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,7 +418,7 @@ export default class Prop implements IVariableSettable {
|
||||
}
|
||||
|
||||
if (mutator && !extraOptions.disableMutator) {
|
||||
mutator.call(this, this.value);
|
||||
mutator.call(this as any, this.value);
|
||||
}
|
||||
|
||||
if (this.modify(force)) {
|
||||
@ -485,7 +485,7 @@ export default class Prop implements IVariableSettable {
|
||||
if (!options || !options.disableMutator) {
|
||||
const mutator = this.config.mutator;
|
||||
if (mutator) {
|
||||
mutator.call(this, value);
|
||||
mutator.call(this as any, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -512,7 +512,7 @@ export default class Prop implements IVariableSettable {
|
||||
|
||||
let hidden = this.config.hidden;
|
||||
if (typeof hidden === 'function') {
|
||||
hidden = hidden.call(this, this.getValue());
|
||||
hidden = hidden.call(this as any, this.getValue());
|
||||
}
|
||||
return hidden === true;
|
||||
}
|
||||
@ -520,7 +520,7 @@ export default class Prop implements IVariableSettable {
|
||||
public isDisabled() {
|
||||
let disabled = this.config.disabled;
|
||||
if (typeof disabled === 'function') {
|
||||
disabled = disabled.call(this, this.getValue());
|
||||
disabled = disabled.call(this as any, this.getValue());
|
||||
}
|
||||
return disabled === true;
|
||||
}
|
||||
@ -530,7 +530,7 @@ export default class Prop implements IVariableSettable {
|
||||
|
||||
let ignore = this.config.ignore;
|
||||
if (typeof ignore === 'function') {
|
||||
ignore = ignore.call(this, this.getValue());
|
||||
ignore = ignore.call(this as any, this.getValue());
|
||||
}
|
||||
return ignore === true;
|
||||
}
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
import { obx, computed } from '@ali/lowcode-globals';
|
||||
import WidgetContainer from './widget/widget-container';
|
||||
import { Skeleton } from './skeleton';
|
||||
import { IWidget } from './widget/widget';
|
||||
import { IWidgetBaseConfig } from './types';
|
||||
|
||||
export default class Area<C extends IWidgetBaseConfig = any, T extends IWidget = IWidget> {
|
||||
@obx private _visible: boolean = true;
|
||||
|
||||
@computed get visible() {
|
||||
if (this.exclusive) {
|
||||
return this.container.current != null;
|
||||
}
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
get current() {
|
||||
if (this.exclusive) {
|
||||
return this.container.current;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
readonly container: WidgetContainer<T, C>;
|
||||
constructor(readonly skeleton: Skeleton, readonly name: string, handle: (item: T | C) => T, private exclusive?: boolean, defaultSetCurrent: boolean = false) {
|
||||
this.container = skeleton.createContainer(name, handle, exclusive, () => this.visible, defaultSetCurrent);
|
||||
}
|
||||
|
||||
@computed isEmpty(): boolean {
|
||||
return this.container.items.length < 1;
|
||||
}
|
||||
|
||||
add(config: T | C): T {
|
||||
return this.container.add(config);
|
||||
}
|
||||
|
||||
private lastCurrent: T | null = null;
|
||||
setVisible(flag: boolean) {
|
||||
if (this.exclusive) {
|
||||
const current = this.container.current;
|
||||
if (flag && !current) {
|
||||
this.container.active(this.lastCurrent || this.container.getAt(0))
|
||||
} else if (current) {
|
||||
this.lastCurrent = current;
|
||||
this.container.unactive(current);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._visible = flag;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setVisible(false);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.setVisible(true);
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
* 拖拽排序有问题
|
||||
* forceInline 有问题
|
||||
* 部分改变不响应
|
||||
* 样式还原
|
||||
* autofocus
|
||||
@ -1,279 +0,0 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import { Icon, Button, Message } from '@alifd/next';
|
||||
import { Title, SetterType, FieldConfig, SetterConfig } from '@ali/lowcode-globals';
|
||||
import { createSettingFieldView } from '../../settings/settings-pane';
|
||||
import { PopupContext, PopupPipe } from '../../popup';
|
||||
import Sortable from './sortable';
|
||||
import './style.less';
|
||||
import { SettingField } from '@ali/lowcode-designer';
|
||||
|
||||
interface ArraySetterState {
|
||||
items: SettingField[];
|
||||
itemsMap: Map<string | number, SettingField>;
|
||||
prevLength: number;
|
||||
}
|
||||
|
||||
interface ArraySetterProps {
|
||||
value: any[];
|
||||
field: SettingField;
|
||||
itemSetter?: SetterType;
|
||||
columns?: FieldConfig[];
|
||||
multiValue?: boolean;
|
||||
}
|
||||
|
||||
export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
|
||||
static getDerivedStateFromProps(props: ArraySetterProps, state: ArraySetterState) {
|
||||
const { value, field } = props;
|
||||
const newLength = value && Array.isArray(value) ? value.length : 0;
|
||||
if (state && state.prevLength === newLength) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// props value length change will go here
|
||||
const originLength = state ? state.items.length : 0;
|
||||
if (state && originLength === newLength) {
|
||||
return {
|
||||
prevLength: newLength,
|
||||
};
|
||||
}
|
||||
|
||||
const itemsMap = state ? state.itemsMap : new Map<string | number, SettingField>();
|
||||
let items = state ? state.items.slice() : [];
|
||||
if (newLength > originLength) {
|
||||
for (let i = originLength; i < newLength; i++) {
|
||||
const item = field.createField({
|
||||
name: i,
|
||||
setter: props.itemSetter,
|
||||
// FIXME:
|
||||
forceInline: 1,
|
||||
});
|
||||
items[i] = item;
|
||||
itemsMap.set(item.id, item);
|
||||
}
|
||||
} else if (newLength < originLength) {
|
||||
const deletes = items.splice(newLength);
|
||||
deletes.forEach((item) => {
|
||||
itemsMap.delete(item.id);
|
||||
});
|
||||
}
|
||||
return {
|
||||
items,
|
||||
itemsMap,
|
||||
prevLength: newLength,
|
||||
};
|
||||
}
|
||||
|
||||
state: ArraySetterState = {
|
||||
items: [],
|
||||
itemsMap: new Map<string | number, SettingField>(),
|
||||
prevLength: 0,
|
||||
};
|
||||
|
||||
onSort(sortedIds: Array<string | number>) {
|
||||
const { itemsMap } = this.state;
|
||||
const items = sortedIds.map((id, index) => {
|
||||
const item = itemsMap.get(id)!;
|
||||
item.setKey(index);
|
||||
return item;
|
||||
});
|
||||
this.setState({
|
||||
items,
|
||||
});
|
||||
}
|
||||
|
||||
private scrollToLast: boolean = false;
|
||||
onAdd() {
|
||||
const { items, itemsMap } = this.state;
|
||||
const { itemSetter } = this.props;
|
||||
const initialValue = typeof itemSetter === 'object' ? (itemSetter as any).initialValue : null;
|
||||
const item = this.props.field.createField({
|
||||
name: items.length,
|
||||
setter: itemSetter,
|
||||
// FIXME:
|
||||
forceInline: 1,
|
||||
});
|
||||
items.push(item);
|
||||
itemsMap.set(item.id, item);
|
||||
item.setValue(typeof initialValue === 'function' ? initialValue(item) : initialValue);
|
||||
this.scrollToLast = true;
|
||||
this.setState({
|
||||
items: items.slice(),
|
||||
});
|
||||
}
|
||||
|
||||
onRemove(field: SettingField) {
|
||||
const { items } = this.state;
|
||||
let i = items.indexOf(field);
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
items.splice(i, 1);
|
||||
const l = items.length;
|
||||
while (i < l) {
|
||||
items[i].setKey(i);
|
||||
i++;
|
||||
}
|
||||
field.remove();
|
||||
this.setState({ items: items.slice() });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.state.items.forEach((field) => {
|
||||
field.purge();
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(_: any, nextState: ArraySetterState) {
|
||||
if (nextState.items !== this.state.items) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
let columns: any = null;
|
||||
if (this.props.columns) {
|
||||
columns = this.props.columns.map((column) => <Title key={column.name} title={column.title || (column.name as string)} />);
|
||||
}
|
||||
|
||||
const { items } = this.state;
|
||||
const scrollToLast = this.scrollToLast;
|
||||
this.scrollToLast = false;
|
||||
const lastIndex = items.length - 1;
|
||||
|
||||
const content =
|
||||
items.length > 0 ? (
|
||||
<div className="lc-setter-list-scroll-body">
|
||||
<Sortable itemClassName="lc-setter-list-card" onSort={this.onSort.bind(this)}>
|
||||
{items.map((field, index) => (
|
||||
<ArrayItem
|
||||
key={field.id}
|
||||
scrollIntoView={scrollToLast && index === lastIndex}
|
||||
field={field}
|
||||
onRemove={this.onRemove.bind(this, field)}
|
||||
/>
|
||||
))}
|
||||
</Sortable>
|
||||
</div>
|
||||
) : this.props.multiValue ? (
|
||||
<Message type="warning">当前选择了多个节点,且值不一致,修改会覆盖所有值</Message>
|
||||
) : (
|
||||
<Message type="notice">当前项目为空</Message>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="lc-setter-list lc-block-setter">
|
||||
{/*<div className="lc-block-setter-actions">
|
||||
<Button size="medium" onClick={this.onAdd.bind(this)}>
|
||||
<Icon type="add" />
|
||||
<span>添加</span>
|
||||
</Button>
|
||||
</div>*/}
|
||||
{columns && <div className="lc-setter-list-columns">{columns}</div>}
|
||||
{content}
|
||||
<Button className="lc-setter-list-add" type="primary" onClick={this.onAdd.bind(this)}>
|
||||
<Icon type="add" />
|
||||
<span>添加一项</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayItem extends Component<{
|
||||
field: SettingField;
|
||||
onRemove: () => void;
|
||||
scrollIntoView: boolean;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
private shell?: HTMLDivElement | null;
|
||||
componentDidMount() {
|
||||
if (this.props.scrollIntoView && this.shell) {
|
||||
this.shell.parentElement!.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { onRemove, field } = this.props;
|
||||
return (
|
||||
<div className="lc-listitem" ref={(ref) => (this.shell = ref)}>
|
||||
<div draggable className="lc-listitem-handler">
|
||||
<Icon type="ellipsis" size="small" />
|
||||
</div>
|
||||
<div className="lc-listitem-body">{createSettingFieldView(field, field.parent)}</div>
|
||||
<div className="lc-listitem-actions">
|
||||
<div className="lc-listitem-action" onClick={onRemove}>
|
||||
<Icon type="ashbin" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TableSetter extends ListSetter {
|
||||
// todo:
|
||||
// forceInline = 1
|
||||
// has more actions
|
||||
}
|
||||
|
||||
export default class ArraySetter extends Component<{
|
||||
value: any[];
|
||||
field: SettingField;
|
||||
itemSetter?: SetterType;
|
||||
mode?: 'popup' | 'list';
|
||||
forceInline?: boolean;
|
||||
multiValue?: boolean;
|
||||
}> {
|
||||
static contextType = PopupContext;
|
||||
private pipe: any;
|
||||
render() {
|
||||
const { mode, forceInline, ...props } = this.props;
|
||||
const { field, itemSetter } = props;
|
||||
let columns: FieldConfig[] | undefined;
|
||||
if ((itemSetter as SetterConfig)?.componentName === 'ObjectSetter') {
|
||||
const items: FieldConfig[] = (itemSetter as any).props?.config?.items;
|
||||
if (items && Array.isArray(items)) {
|
||||
columns = items.filter((item) => item.isRequired || item.important || (item.setter as any)?.isRequired);
|
||||
if (columns.length > 4) {
|
||||
columns = columns.slice(0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'popup' || forceInline) {
|
||||
const title = (
|
||||
<Fragment>
|
||||
编辑:
|
||||
<Title title={field.title} />
|
||||
</Fragment>
|
||||
);
|
||||
if (!this.pipe) {
|
||||
let width = 360;
|
||||
if (columns) {
|
||||
if (columns.length === 3) {
|
||||
width = 480;
|
||||
} else if (columns.length > 3) {
|
||||
width = 600;
|
||||
}
|
||||
}
|
||||
this.pipe = (this.context as PopupPipe).create({ width });
|
||||
}
|
||||
this.pipe.send(<TableSetter key={field.id} {...props} columns={columns} />, title);
|
||||
return (
|
||||
<Button
|
||||
type={forceInline ? 'normal' : 'primary'}
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target, field.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
{forceInline ? title : '编辑数组'}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return <ListSetter {...props} columns={columns?.slice(0, 2)} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
.lc-sortable {
|
||||
position: relative;
|
||||
|
||||
.lc-sortable-card {
|
||||
box-sizing: border-box;
|
||||
&:after, &:before {
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
&.lc-dragging {
|
||||
outline: 2px dashed var(--color-brand);
|
||||
outline-offset: -2px;
|
||||
> * {
|
||||
visibility: hidden;
|
||||
}
|
||||
border-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
[draggable] {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
}
|
||||
@ -1,220 +0,0 @@
|
||||
import { Component, Children, ReactElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './sortable.less';
|
||||
|
||||
class Sortable extends Component<{
|
||||
className?: string;
|
||||
itemClassName?: string;
|
||||
onSort?: (sortedIds: Array<string | number>) => void;
|
||||
dragImageSourceHandler?: (elem: Element) => Element;
|
||||
children: ReactElement[];
|
||||
}> {
|
||||
private shell?: HTMLDivElement | null;
|
||||
private items?: Array<string | number>;
|
||||
private willDetach?: () => void;
|
||||
componentDidMount() {
|
||||
const box = this.shell!;
|
||||
|
||||
let isDragEnd: boolean = false;
|
||||
|
||||
/**
|
||||
* target node to be dragged
|
||||
*/
|
||||
let source: Element | null;
|
||||
|
||||
/**
|
||||
* node to be placed
|
||||
*/
|
||||
let ref: Element | null;
|
||||
|
||||
/**
|
||||
* next sibling of the source node
|
||||
*/
|
||||
let origRef: Element | null;
|
||||
|
||||
/**
|
||||
* accurately locate the node from event
|
||||
*/
|
||||
const locate = (e: DragEvent) => {
|
||||
let y = e.clientY;
|
||||
if (e.view !== window && e.view!.frameElement) {
|
||||
y += e.view!.frameElement.getBoundingClientRect().top;
|
||||
}
|
||||
let node = box.firstElementChild as HTMLDivElement;
|
||||
while (node) {
|
||||
if (node !== source && node.dataset.id) {
|
||||
const rect = node.getBoundingClientRect();
|
||||
|
||||
if (rect.height <= 0) continue;
|
||||
if (y < rect.top + rect.height / 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node = node.nextElementSibling as HTMLDivElement;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* find the source node
|
||||
*/
|
||||
const getSource = (e: DragEvent) => {
|
||||
const target = e.target as Element;
|
||||
if (!target || !box.contains(target) || target === box) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let node = box.firstElementChild;
|
||||
while (node) {
|
||||
if (node.contains(target)) {
|
||||
return node;
|
||||
}
|
||||
node = node.nextElementSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const sort = (beforeId: string | number | null | undefined) => {
|
||||
if (!source) return;
|
||||
|
||||
const sourceId = (source as HTMLDivElement).dataset.id;
|
||||
const items = this.items!;
|
||||
const origIndex = items.findIndex(id => id == sourceId);
|
||||
|
||||
let newIndex = beforeId ? items.findIndex(id => id == beforeId) : items.length;
|
||||
|
||||
if (origIndex < 0 || newIndex < 0) return;
|
||||
if (this.props.onSort) {
|
||||
if (newIndex > origIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
if (origIndex === newIndex) return;
|
||||
const item = items.splice(origIndex, 1);
|
||||
items.splice(newIndex, 0, item[0]);
|
||||
|
||||
this.props.onSort(items);
|
||||
}
|
||||
};
|
||||
|
||||
const dragstart = (e: DragEvent) => {
|
||||
isDragEnd = false;
|
||||
source = getSource(e);
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
origRef = source.nextElementSibling;
|
||||
const rect = source.getBoundingClientRect();
|
||||
let dragSource = source;
|
||||
if (this.props.dragImageSourceHandler) {
|
||||
dragSource = this.props.dragImageSourceHandler(source);
|
||||
}
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.setDragImage(dragSource, e.clientX - rect.left, e.clientY - rect.top);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
try {
|
||||
e.dataTransfer.setData('application/json', {} as any);
|
||||
} catch (ex) {
|
||||
// eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
source!.classList.add('lc-dragging');
|
||||
}, 0);
|
||||
return true;
|
||||
};
|
||||
|
||||
const placeAt = (beforeRef: Element | null) => {
|
||||
if (beforeRef) {
|
||||
if (beforeRef !== source) {
|
||||
box.insertBefore(source!, beforeRef);
|
||||
}
|
||||
} else {
|
||||
box.appendChild(source!);
|
||||
}
|
||||
};
|
||||
|
||||
const adjust = (e: DragEvent) => {
|
||||
if (isDragEnd) return;
|
||||
ref = locate(e);
|
||||
placeAt(ref);
|
||||
};
|
||||
|
||||
let lastDragEvent: DragEvent | null;
|
||||
const drag = (e: DragEvent) => {
|
||||
if (!source) return;
|
||||
e.preventDefault();
|
||||
if (lastDragEvent) {
|
||||
if (lastDragEvent.clientX === e.clientX && lastDragEvent.clientY === e.clientY) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
lastDragEvent = e;
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
}
|
||||
adjust(e);
|
||||
};
|
||||
|
||||
const dragend = (e: DragEvent) => {
|
||||
isDragEnd = true;
|
||||
if (!source) return;
|
||||
e.preventDefault();
|
||||
source.classList.remove('lc-dragging');
|
||||
placeAt(origRef);
|
||||
sort(ref ? (ref as HTMLDivElement).dataset.id : null);
|
||||
source = null;
|
||||
ref = null;
|
||||
origRef = null;
|
||||
lastDragEvent = null;
|
||||
};
|
||||
|
||||
box.addEventListener('dragstart', dragstart);
|
||||
document.addEventListener('dragover', drag);
|
||||
document.addEventListener('drag', drag);
|
||||
document.addEventListener('dragend', dragend);
|
||||
|
||||
this.willDetach = () => {
|
||||
box.removeEventListener('dragstart', dragstart);
|
||||
document.removeEventListener('dragover', drag);
|
||||
document.removeEventListener('drag', drag);
|
||||
document.removeEventListener('dragend', dragend);
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.willDetach) {
|
||||
this.willDetach();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, itemClassName, children } = this.props;
|
||||
const items: Array<string | number> = [];
|
||||
const cards = Children.map(children, child => {
|
||||
const id = child.key!;
|
||||
items.push(id);
|
||||
return (
|
||||
<div key={id} data-id={id} className={classNames('lc-sortable-card', itemClassName)}>
|
||||
{child}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
this.items = items;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('lc-sortable', className)}
|
||||
ref={ref => {
|
||||
this.shell = ref;
|
||||
}}
|
||||
>
|
||||
{cards}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Sortable;
|
||||
@ -1,103 +0,0 @@
|
||||
.lc-setter-list {
|
||||
[draggable] {
|
||||
cursor: move;
|
||||
}
|
||||
color: var(--color-text);
|
||||
|
||||
.next-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
line-height: 1 !important;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.lc-setter-list-add {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 8px;;
|
||||
}
|
||||
|
||||
|
||||
.lc-setter-list-columns {
|
||||
display: flex;
|
||||
> .lc-title {
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
margin-left: 47px;
|
||||
margin-right: 28px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.lc-setter-list-scroll-body {
|
||||
margin: -8px -5px;
|
||||
padding: 8px 10px;
|
||||
overflow-y: auto;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.lc-setter-list-card {
|
||||
border: 1px solid rgba(31,56,88,.2);
|
||||
background-color: var(--color-block-background-light);
|
||||
border-radius: 3px;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.lc-listitem {
|
||||
position: relative;
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 34px;
|
||||
|
||||
.lc-listitem-actions {
|
||||
margin: 0 3px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
.lc-listitem-action {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.lc-listitem-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
text-overflow: ellipsis;
|
||||
.lc-field {
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
>.lc-field-body {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
> * {
|
||||
width: 100%;
|
||||
}
|
||||
.next-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.lc-listitem-handler {
|
||||
margin-left: 2px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
.next-icon-ellipsis {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,184 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon } from '@alifd/next';
|
||||
import { Title, TitleContent } from '@ali/lowcode-globals';
|
||||
import { PopupPipe, PopupContext } from '../popup';
|
||||
import './index.less';
|
||||
|
||||
export interface FieldProps {
|
||||
className?: string;
|
||||
title?: TitleContent | null;
|
||||
defaultDisplay?: 'accordion' | 'inline' | 'block';
|
||||
collapsed?: boolean;
|
||||
onExpandChange?: (expandState: boolean) => void;
|
||||
}
|
||||
|
||||
export class Field extends Component<FieldProps> {
|
||||
state = {
|
||||
collapsed: this.props.collapsed,
|
||||
display: this.props.defaultDisplay || 'inline',
|
||||
};
|
||||
|
||||
private toggleExpand = () => {
|
||||
const { onExpandChange } = this.props;
|
||||
const collapsed = !this.state.collapsed;
|
||||
this.setState({
|
||||
collapsed,
|
||||
});
|
||||
onExpandChange && onExpandChange(!collapsed);
|
||||
};
|
||||
private body: HTMLDivElement | null = null;
|
||||
private dispose?: () => void;
|
||||
private deployBlockTesting() {
|
||||
if (this.dispose) {
|
||||
this.dispose();
|
||||
}
|
||||
const body = this.body;
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
const check = () => {
|
||||
const setter = body.firstElementChild;
|
||||
if (setter && setter.classList.contains('lc-block-setter')) {
|
||||
this.setState({
|
||||
display: 'block',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
display: 'inline',
|
||||
});
|
||||
}
|
||||
};
|
||||
const observer = new MutationObserver(check);
|
||||
check();
|
||||
observer.observe(body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
this.dispose = () => observer.disconnect();
|
||||
}
|
||||
componentDidMount() {
|
||||
const { defaultDisplay } = this.props;
|
||||
if (!defaultDisplay || defaultDisplay === 'inline') {
|
||||
this.deployBlockTesting();
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if (this.dispose) {
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, children, title } = this.props;
|
||||
const { display, collapsed } = this.state;
|
||||
const isAccordion = display === 'accordion';
|
||||
return (
|
||||
<div
|
||||
className={classNames(`lc-field lc-${display}-field`, className, {
|
||||
'lc-field-is-collapsed': isAccordion && collapsed,
|
||||
})}
|
||||
>
|
||||
<div className="lc-field-head" onClick={isAccordion ? this.toggleExpand : undefined}>
|
||||
<div className="lc-field-title">
|
||||
<Title title={title || ''} />
|
||||
</div>
|
||||
{isAccordion && <Icon className="lc-field-icon" type="arrow-up" size="xs" />}
|
||||
</div>
|
||||
<div key="body" ref={(shell) => (this.body = shell)} className="lc-field-body">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface PopupFieldProps extends FieldProps {
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export class PopupField extends Component<PopupFieldProps> {
|
||||
static contextType = PopupContext;
|
||||
private pipe: any;
|
||||
|
||||
static defaultProps: PopupFieldProps = {
|
||||
width: 300,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, children, title, width } = this.props;
|
||||
if (!this.pipe) {
|
||||
this.pipe = (this.context as PopupPipe).create({ width });
|
||||
}
|
||||
|
||||
const titleElement = title && (
|
||||
<div className="lc-field-title">
|
||||
<Title title={title} />
|
||||
</div>
|
||||
);
|
||||
|
||||
this.pipe.send(<div className="lc-field-body">{children}</div>, titleElement);
|
||||
|
||||
return (
|
||||
<div className={classNames('lc-field lc-popup-field', className)}>
|
||||
{title && (
|
||||
<div
|
||||
className="lc-field-head"
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target);
|
||||
}}
|
||||
>
|
||||
<div className="lc-field-title">
|
||||
<Title title={title} />
|
||||
</div>
|
||||
<Icon className="lc-field-icon" type="arrow-left" size="xs" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface EntryFieldProps extends FieldProps {
|
||||
stageName?: string;
|
||||
}
|
||||
|
||||
export class EntryField extends Component<EntryFieldProps> {
|
||||
render() {
|
||||
const { stageName, title, className } = this.props;
|
||||
const classNameList = classNames('engine-setting-field', 'engine-entry-field', className);
|
||||
const fieldProps: any = {};
|
||||
|
||||
if (stageName) {
|
||||
// 为 stage 切换奠定基础
|
||||
fieldProps['data-stage-target'] = stageName;
|
||||
}
|
||||
|
||||
const innerElements = [
|
||||
<span className="engine-field-title" key="field-title">
|
||||
{title}
|
||||
</span>,
|
||||
// renderTip(tip, { propName }),
|
||||
// <Icons name="arrow" className="engine-field-arrow" size="12px" key="engine-field-arrow-icon" />,
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={classNameList} {...fieldProps}>
|
||||
{innerElements}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class PlainField extends Component<FieldProps> {
|
||||
render() {
|
||||
const { className, children } = this.props;
|
||||
return (
|
||||
<div className={classNames(`lc-field lc-plain-field`, className)}>
|
||||
<div className="lc-field-body">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,154 +0,0 @@
|
||||
@import '../variables.less';
|
||||
|
||||
@x-gap: 10px;
|
||||
@y-gap: 8px;
|
||||
|
||||
.lc-field {
|
||||
// head
|
||||
.lc-field-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.lc-field-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.lc-field-icon {
|
||||
margin-right: @x-gap;
|
||||
transform-origin: center;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-plain-field {
|
||||
// for top-level style
|
||||
padding: 8px 10px;
|
||||
> .lc-field-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-inline-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// for top-level style
|
||||
padding: 8px 10px;
|
||||
|
||||
> .lc-field-head {
|
||||
width: 70px;
|
||||
margin-right: 1px;
|
||||
.lc-title-label {
|
||||
width: 70px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
> .lc-field-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-block-field, &.lc-accordion-field {
|
||||
display: block;
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid var(--color-line-normal, @dark-alpha-2);
|
||||
}
|
||||
> .lc-field-head {
|
||||
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, @dark-alpha-2);
|
||||
color: var(--color-title, @white-alpha-2);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
> .lc-field-body {
|
||||
padding: @y-gap @x-gap/2;
|
||||
}
|
||||
|
||||
+ .lc-inline-field {
|
||||
border-top: 1px solid var(--color-line-normal, @dark-alpha-2);
|
||||
}
|
||||
}
|
||||
|
||||
.lc-setter-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.lc-block-field {
|
||||
position: relative;
|
||||
>.lc-field-body>.lc-block-setter>.lc-setter-actions {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.lc-accordion-field {
|
||||
// collapsed
|
||||
&.lc-field-is-collapsed {
|
||||
> .lc-field-head .lc-field-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
> .lc-field-body {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 邻近的保持上下距离
|
||||
+ .lc-field {
|
||||
margin-top: @y-gap;
|
||||
}
|
||||
}
|
||||
|
||||
// 2rd level reset
|
||||
.lc-field-body {
|
||||
.lc-inline-field {
|
||||
padding: @y-gap @x-gap/2 0 @x-gap/2;
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
+ .lc-accordion-field, +.lc-block-field {
|
||||
margin-top: @y-gap;
|
||||
}
|
||||
}
|
||||
|
||||
.lc-field {
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
.lc-accordion-field, .lc-block-field {
|
||||
> .lc-field-head {
|
||||
padding-left: @x-gap/2;
|
||||
background: var(--color-block-background-light);
|
||||
border-bottom-color: var(--color-line-light);
|
||||
> .lc-field-icon {
|
||||
margin-right: @x-gap/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3rd level field title width should short
|
||||
.lc-field-body .lc-inline-field {
|
||||
> .lc-field-head {
|
||||
width: 50px;
|
||||
.lc-title-label {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { ReactNode, createElement } from 'react';
|
||||
import { TitleContent } from '@ali/lowcode-globals';
|
||||
import './index.less';
|
||||
import { Field, PopupField, EntryField, PlainField } from './fields';
|
||||
|
||||
export interface FieldProps {
|
||||
className?: string;
|
||||
title?: TitleContent | null;
|
||||
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
|
||||
collapsed?: boolean;
|
||||
onExpandChange?: (collapsed: boolean) => void;
|
||||
[extra: string]: any;
|
||||
}
|
||||
|
||||
export function createField(props: FieldProps, children: ReactNode, type?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry') {
|
||||
if (type === 'popup') {
|
||||
return createElement(PopupField, props, children);
|
||||
}
|
||||
if (type === 'entry') {
|
||||
return createElement(EntryField, props, children);
|
||||
}
|
||||
if (type === 'plain' || !props.title) {
|
||||
return createElement(PlainField, props, children);
|
||||
}
|
||||
return createElement(Field, { ...props, defaultDisplay: type }, children);
|
||||
}
|
||||
|
||||
export { Field, PopupField, EntryField, PlainField };
|
||||
@ -1,257 +0,0 @@
|
||||
import React, { Component, isValidElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Dropdown, Button, Menu } from '@alifd/next';
|
||||
import {
|
||||
getSetter,
|
||||
getSettersMap,
|
||||
SetterConfig,
|
||||
computed,
|
||||
obx,
|
||||
CustomView,
|
||||
DynamicProps,
|
||||
DynamicSetter,
|
||||
TitleContent,
|
||||
isSetterConfig,
|
||||
Title,
|
||||
createSetterContent,
|
||||
observer,
|
||||
isDynamicSetter,
|
||||
shallowIntl,
|
||||
EmbedTip,
|
||||
isI18nData,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { IconConvert } from '../../icons/convert';
|
||||
|
||||
import './style.less';
|
||||
import { SettingField } from '@ali/lowcode-designer';
|
||||
|
||||
export interface SetterItem {
|
||||
name: string;
|
||||
title: TitleContent;
|
||||
setter: string | DynamicSetter | CustomView;
|
||||
props?: object | DynamicProps;
|
||||
condition?: (field: SettingField) => boolean;
|
||||
initialValue?: any | ((field: SettingField) => any);
|
||||
list: boolean;
|
||||
}
|
||||
|
||||
function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | DynamicSetter>): SetterItem[] {
|
||||
if (!setters) {
|
||||
const normalized: SetterItem[] = [];
|
||||
getSettersMap().forEach((setter, name) => {
|
||||
if (name === 'MixedSetter') {
|
||||
return;
|
||||
}
|
||||
normalized.push({
|
||||
name,
|
||||
title: setter.title || name,
|
||||
setter: name,
|
||||
condition: setter.condition,
|
||||
initialValue: setter.initialValue,
|
||||
list: setter.recommend || false,
|
||||
});
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
const names: string[] = [];
|
||||
function generateName(n: string) {
|
||||
let idx = 1;
|
||||
let got = n;
|
||||
while (names.indexOf(got) > -1) {
|
||||
got = `${n}:${idx++}`;
|
||||
}
|
||||
names.push(got);
|
||||
return got;
|
||||
}
|
||||
return setters.map((setter) => {
|
||||
const config: any = {
|
||||
setter,
|
||||
list: true,
|
||||
};
|
||||
if (isSetterConfig(setter)) {
|
||||
config.setter = setter.componentName;
|
||||
config.props = setter.props;
|
||||
config.condition = setter.condition;
|
||||
config.initialValue = setter.initialValue;
|
||||
config.title = setter.title;
|
||||
}
|
||||
if (typeof config.setter === 'string') {
|
||||
config.name = config.setter;
|
||||
names.push(config.name);
|
||||
const info = getSetter(config.setter);
|
||||
if (!config.title) {
|
||||
config.title = info?.title || config.setter;
|
||||
}
|
||||
if (!config.condition) {
|
||||
config.condition = info?.condition;
|
||||
}
|
||||
if (!config.initialValue) {
|
||||
config.initialValue = info?.initialValue;
|
||||
}
|
||||
} else {
|
||||
config.name = generateName((config.setter as any).displayName || (config.setter as any).name || 'CustomSetter');
|
||||
if (!config.title) {
|
||||
config.title = config.name;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class MixedSetter extends Component<{
|
||||
field: SettingField;
|
||||
setters?: Array<string | SetterConfig | CustomView | DynamicSetter>;
|
||||
onSetterChange?: (field: SettingField, name: string) => void;
|
||||
onChange?: (val: any) => void;
|
||||
value?: any;
|
||||
className?: string;
|
||||
}> {
|
||||
private setters = nomalizeSetters(this.props.setters);
|
||||
@obx.ref private used?: string;
|
||||
@computed private getCurrentSetter() {
|
||||
const { field } = this.props;
|
||||
let firstMatched: SetterItem | undefined;
|
||||
for (const setter of this.setters) {
|
||||
const matched = !setter.condition || setter.condition(field);
|
||||
if (matched) {
|
||||
if (setter.name === this.used) {
|
||||
return setter;
|
||||
}
|
||||
if (!firstMatched) {
|
||||
firstMatched = setter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return firstMatched;
|
||||
}
|
||||
|
||||
private useSetter = (name: string) => {
|
||||
if (name === this.used) {
|
||||
return;
|
||||
}
|
||||
const { field, onChange } = this.props;
|
||||
const setter = this.setters.find((item) => item.name === name);
|
||||
this.used = name;
|
||||
if (setter) {
|
||||
let newValue: any = setter.initialValue;
|
||||
if (newValue && typeof newValue === 'function') {
|
||||
newValue = newValue(field);
|
||||
}
|
||||
onChange && onChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
private shell: HTMLDivElement | null = null;
|
||||
private checkIsBlockField() {
|
||||
if (this.shell) {
|
||||
const setter = this.shell.firstElementChild;
|
||||
if (setter && setter.classList.contains('lc-block-setter')) {
|
||||
this.shell.classList.add('lc-block-setter');
|
||||
} else {
|
||||
this.shell.classList.remove('lc-block-setter');
|
||||
}
|
||||
}
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
componentDidMount() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, field, setters, onSetterChange, ...restProps } = this.props;
|
||||
|
||||
const currentSetter = this.getCurrentSetter();
|
||||
const isTwoType = this.setters.length < 3;
|
||||
|
||||
let setterContent: any;
|
||||
const triggerTitle: any = {
|
||||
tip: {
|
||||
type: 'i18n',
|
||||
'zh-CN': '切换格式',
|
||||
'en-US': 'Switch Format',
|
||||
},
|
||||
icon: <IconConvert size={24} />,
|
||||
};
|
||||
if (currentSetter) {
|
||||
const { setter, title, props } = currentSetter;
|
||||
let setterProps: any = {};
|
||||
let setterType: any;
|
||||
if (isDynamicSetter(setter)) {
|
||||
setterType = setter.call(field, field);
|
||||
} else {
|
||||
setterType = setter;
|
||||
}
|
||||
if (props) {
|
||||
setterProps = props;
|
||||
if (typeof setterProps === 'function') {
|
||||
setterProps = setterProps(field);
|
||||
}
|
||||
}
|
||||
|
||||
setterContent = createSetterContent(setterType, {
|
||||
...shallowIntl(setterProps),
|
||||
field,
|
||||
...restProps,
|
||||
});
|
||||
if (title) {
|
||||
if (typeof title !== 'object' || isI18nData(title) || isValidElement(title)) {
|
||||
triggerTitle.tip = title;
|
||||
} else {
|
||||
triggerTitle.tip = title.tip || title.label;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 未匹配的 null 值,显示 NullValue 空值
|
||||
// 未匹配的 其它 值,显示 InvalidValue 非法值
|
||||
if (restProps.value == null) {
|
||||
setterContent = <span>NullValue</span>;
|
||||
} else {
|
||||
setterContent = <span>InvalidValue</span>;
|
||||
}
|
||||
}
|
||||
const usedName = currentSetter?.name || this.used;
|
||||
let moreBtnNode = (
|
||||
<Title
|
||||
title={triggerTitle}
|
||||
className="lc-switch-trigger"
|
||||
onClick={
|
||||
isTwoType
|
||||
? () => {
|
||||
if (this.setters[0]?.name === usedName) {
|
||||
this.useSetter(this.setters[1]?.name);
|
||||
} else {
|
||||
this.useSetter(this.setters[0]?.name);
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
if (!isTwoType) {
|
||||
moreBtnNode = (
|
||||
<Dropdown trigger={moreBtnNode} triggerType="click" align="tr br">
|
||||
<Menu selectMode="single" hasSelectedIcon={true} selectedKeys={usedName} onItemClick={this.useSetter}>
|
||||
{this.setters.filter(setter => setter.list || setter.name === usedName).map((setter) => {
|
||||
return (
|
||||
<Menu.Item key={setter.name}>
|
||||
<Title title={setter.title} />
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={(shell) => (this.shell = shell)} className={classNames('lc-setter-mixed', className)}>
|
||||
{setterContent}
|
||||
|
||||
<div className="lc-setter-actions">{moreBtnNode}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
.lc-setter-mixed {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-right: 26px;
|
||||
display: block;
|
||||
position: relative;
|
||||
>.lc-setter-actions {
|
||||
position: absolute;
|
||||
right: -2px;
|
||||
top: 50%;
|
||||
transform: translate(100%, -50%);
|
||||
.lc-switch-trigger {
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.next-input,.next-date-picker {
|
||||
width: 100%;
|
||||
}
|
||||
&.lc-block-setter {
|
||||
position: static;
|
||||
margin-right: 0;
|
||||
>.lc-setter-actions {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,180 +0,0 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import { Icon, Button } from '@alifd/next';
|
||||
import { Title, SetterType, FieldConfig } from '@ali/lowcode-globals';
|
||||
import { createSettingFieldView } from '../../settings/settings-pane';
|
||||
import { PopupContext, PopupPipe } from '../../popup';
|
||||
import { SettingField } from '@ali/lowcode-designer';
|
||||
import './style.less';
|
||||
|
||||
export default class ObjectSetter extends Component<{
|
||||
field: SettingField;
|
||||
descriptor?: string | ((rowField: SettingField) => string);
|
||||
config: ObjectSetterConfig;
|
||||
mode?: 'popup' | 'form';
|
||||
// 1: in tablerow 2: in listrow 3: in column-cell
|
||||
forceInline?: number;
|
||||
}> {
|
||||
render() {
|
||||
const { mode, forceInline = 0, ...props } = this.props;
|
||||
if (forceInline || mode === 'popup') {
|
||||
if (forceInline > 2 || mode === 'popup') {
|
||||
// popup
|
||||
return <RowSetter {...props} primaryButton={forceInline ? false : true} />;
|
||||
} else {
|
||||
return <RowSetter columns={forceInline > 1 ? 2 : 4} {...props} />;
|
||||
}
|
||||
} else {
|
||||
// form
|
||||
return <FormSetter {...props} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ObjectSetterConfig {
|
||||
items?: FieldConfig[];
|
||||
extraSetter?: SetterType;
|
||||
}
|
||||
|
||||
interface RowSetterProps {
|
||||
field: SettingField;
|
||||
descriptor?: string | ((rowField: SettingField) => string);
|
||||
config: ObjectSetterConfig;
|
||||
columns?: number;
|
||||
primaryButton?: boolean;
|
||||
}
|
||||
|
||||
class RowSetter extends Component<RowSetterProps> {
|
||||
static contextType = PopupContext;
|
||||
|
||||
state: any = {
|
||||
descriptor: '',
|
||||
};
|
||||
|
||||
private items?: SettingField[];
|
||||
constructor(props: RowSetterProps) {
|
||||
super(props);
|
||||
const { config, descriptor, field, columns } = props;
|
||||
const items: SettingField[] = [];
|
||||
if (columns && config.items) {
|
||||
const l = Math.min(config.items.length, columns);
|
||||
for (let i = 0; i < l; i++) {
|
||||
const conf = config.items[i];
|
||||
if (conf.isRequired || conf.important || (conf.setter as any)?.isRequired) {
|
||||
const item = field.createField({
|
||||
...conf,
|
||||
// in column-cell
|
||||
forceInline: 3,
|
||||
});
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.length > 0) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
let firstRun: boolean = true;
|
||||
field.onEffect(() => {
|
||||
let state: any = {};
|
||||
if (descriptor) {
|
||||
if (typeof descriptor === 'function') {
|
||||
state.descriptor = descriptor(field);
|
||||
} else {
|
||||
state.descriptor = field.getPropValue(descriptor);
|
||||
}
|
||||
} else {
|
||||
state.descriptor = field.title;
|
||||
}
|
||||
|
||||
if (firstRun) {
|
||||
firstRun = false;
|
||||
this.state = state;
|
||||
} else {
|
||||
this.setState(state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(_: any, nextState: any) {
|
||||
if (this.state.decriptor !== nextState.decriptor) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private pipe: any;
|
||||
render() {
|
||||
const items = this.items;
|
||||
const { field, primaryButton, config } = this.props;
|
||||
|
||||
if (!this.pipe) {
|
||||
this.pipe = (this.context as PopupPipe).create({ width: 320 });
|
||||
}
|
||||
|
||||
const title = (
|
||||
<Fragment>
|
||||
编辑:
|
||||
<Title title={this.state.descriptor} />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
this.pipe.send(<FormSetter key={field.id} field={field} config={config} />, title);
|
||||
|
||||
if (items) {
|
||||
return (
|
||||
<div className="lc-setter-object-row">
|
||||
<div
|
||||
className="lc-setter-object-row-edit"
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target, field.id);
|
||||
}}
|
||||
>
|
||||
<Icon size="small" type="edit" />
|
||||
</div>
|
||||
<div className="lc-setter-object-row-body">{items.map((item) => createSettingFieldView(item, field))}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
type={primaryButton === false ? 'normal' : 'primary'}
|
||||
onClick={(e) => {
|
||||
this.pipe.show((e as any).target, field.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface FormSetterProps {
|
||||
field: SettingField;
|
||||
config: ObjectSetterConfig;
|
||||
}
|
||||
class FormSetter extends Component<FormSetterProps> {
|
||||
private items: SettingField[];
|
||||
constructor(props: RowSetterProps) {
|
||||
super(props);
|
||||
const { config, field } = props;
|
||||
this.items = (config.items || []).map((conf) => field.createField(conf));
|
||||
|
||||
// TODO: extraConfig for custom fields
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { field } = this.props;
|
||||
return (
|
||||
<div className="lc-setter-object lc-block-setter">
|
||||
{this.items.map((item, index) => createSettingFieldView(item, field, index))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
.lc-setter-object-row {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
.lc-setter-object-row-edit {
|
||||
width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.lc-setter-object-row-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
.lc-field {
|
||||
padding: 0 !important;
|
||||
.lc-field-body {
|
||||
padding: 0 !important; margin: 0 !important;
|
||||
}
|
||||
}
|
||||
> * {
|
||||
flex: 1;
|
||||
flex-shrink: 1;
|
||||
margin-left: 2px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user