mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-25 14:53:57 +00:00
daily tag
This commit is contained in:
parent
9e26b961eb
commit
c9d139bf30
@ -38,16 +38,16 @@
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/react": "^16.9.13",
|
||||
"@types/react-dom": "^16.9.4",
|
||||
"build-plugin-component": "^0.2.0",
|
||||
"build-plugin-component": "^0.2.7-1",
|
||||
"build-plugin-fusion": "^0.1.0",
|
||||
"build-plugin-moment-locales": "^0.1.0",
|
||||
"eslint": "^6.0.1",
|
||||
"prettier": "^1.19.1",
|
||||
"react": "^16.3.0",
|
||||
"react-dom": "^16.3.0"
|
||||
"react": "^16.8.0",
|
||||
"react-dom": "^16.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.3.0",
|
||||
"react": "^16.8.0",
|
||||
"@alifd/next": "1.x"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
48
packages/editor-framework/src/definitions.ts
Normal file
48
packages/editor-framework/src/definitions.ts
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
export interface EditorConfig {
|
||||
|
||||
};
|
||||
|
||||
export interface NpmConfig {
|
||||
version: string,
|
||||
package: string,
|
||||
main?: string,
|
||||
exportName?: string,
|
||||
subName?: string,
|
||||
destructuring?: boolean
|
||||
};
|
||||
|
||||
export interface SkeletonConfig {
|
||||
config: NpmConfig,
|
||||
props?: object,
|
||||
handler?: (EditorConfig) => EditorConfig
|
||||
};
|
||||
|
||||
export interface FusionTheme {
|
||||
package: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
export interface ThemeConfig {
|
||||
fusion?: FusionTheme
|
||||
}
|
||||
|
||||
export interface PluginsConfig {
|
||||
[key]: Array<PluginConfig>
|
||||
};
|
||||
|
||||
export interface PluginConfig {
|
||||
pluginKey: string,
|
||||
type: string,
|
||||
props: object,
|
||||
config: NpmConfig,
|
||||
pluginProps: object
|
||||
};
|
||||
|
||||
export type HooksConfig = Array<HookConfig>;
|
||||
|
||||
export interface HookConfig {
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -10,6 +10,48 @@ export interface pluginProps {
|
||||
messages: object
|
||||
}
|
||||
|
||||
export default function plugin(Comp) {
|
||||
|
||||
class Plugin extends PureComponent<pluginProps> {
|
||||
static displayName = 'lowcode-editor-plugin';
|
||||
static defaultProps = {
|
||||
config: {}
|
||||
};
|
||||
static contextType = EditorContext;
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
if (isEmpty(props.config) || !props.config.pluginKey) {
|
||||
console.warn('lowcode editor plugin has wrong config');
|
||||
return;
|
||||
}
|
||||
|
||||
const { locale, messages, editor } = props;
|
||||
// 注册插件
|
||||
this.editor = editor;
|
||||
this.i18n = generateI18n(locale, messages);
|
||||
this.pluginKey = props.config.pluginKey;
|
||||
editor.plugins = editor.plugins || {};
|
||||
editor.plugins[this.pluginKey] = this;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// 销毁插件
|
||||
if (this.editor && this.editor.plugins) {
|
||||
delete this.editor.plugins[this.pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
config
|
||||
} = this.props;
|
||||
return <Comp i18n={this.i18n} editor={this.editor} config={config} {...config.pluginProps}/>
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class Plugin extends PureComponent<pluginProps> {
|
||||
|
||||
@ -213,5 +213,30 @@ export function transformToPromise(input) {
|
||||
}
|
||||
|
||||
export function comboEditorConfig(defaultConfig, customConfig) {
|
||||
|
||||
const { ideConfig = {}, utils = {} } = this.props;
|
||||
const comboShortCuts = () => {
|
||||
const defaultShortCuts = defaultIdeConfig.shortCuts;
|
||||
const shortCuts = ideConfig.shortCuts || [];
|
||||
const configMap = skeletonUtils.transformArrayToMap(defaultShortCuts, 'keyboard');
|
||||
(shortCuts || []).forEach(item => {
|
||||
configMap[item.keyboard] = item;
|
||||
});
|
||||
return Object.keys(configMap).map(key => configMap[key]);
|
||||
};
|
||||
return {
|
||||
...ideConfig,
|
||||
utils: {
|
||||
...skeletonUtils,
|
||||
...utils
|
||||
},
|
||||
constants: {
|
||||
...defaultIdeConfig.constants,
|
||||
...ideConfig.constants
|
||||
},
|
||||
extensions: {
|
||||
...defaultIdeConfig.extensions,
|
||||
...ideConfig.extensions
|
||||
},
|
||||
shortCuts: comboShortCuts()
|
||||
};
|
||||
}
|
||||
30
packages/editor-skeleton/es/components/LeftPlugin/index.d.ts
vendored
Normal file
30
packages/editor-skeleton/es/components/LeftPlugin/index.d.ts
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
import { PureComponent } from 'react';
|
||||
import './index.scss';
|
||||
export default class LeftAddon extends PureComponent {
|
||||
static displayName: string;
|
||||
static propTypes: {
|
||||
active: any;
|
||||
config: any;
|
||||
disabled: any;
|
||||
dotted: any;
|
||||
locked: any;
|
||||
onClick: any;
|
||||
};
|
||||
static defaultProps: {
|
||||
active: boolean;
|
||||
config: {};
|
||||
disabled: boolean;
|
||||
dotted: boolean;
|
||||
locked: boolean;
|
||||
onClick: () => void;
|
||||
};
|
||||
static contextType: any;
|
||||
constructor(props: any, context: any);
|
||||
componentDidMount(): void;
|
||||
componentWillUnmount(): void;
|
||||
handleClose: () => void;
|
||||
handleOpen: () => void;
|
||||
handleShow: () => void;
|
||||
renderIcon: (clickCallback: any) => JSX.Element;
|
||||
render(): JSX.Element;
|
||||
}
|
||||
@ -0,0 +1,259 @@
|
||||
import _extends from "@babel/runtime/helpers/extends";
|
||||
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import AppContext from '@ali/iceluna-sdk/lib/context/appContext';
|
||||
import { Balloon, Dialog, Icon, Badge } from '@alife/next';
|
||||
import './index.scss';
|
||||
|
||||
var LeftAddon = /*#__PURE__*/function (_PureComponent) {
|
||||
_inheritsLoose(LeftAddon, _PureComponent);
|
||||
|
||||
function LeftAddon(_props, context) {
|
||||
var _this;
|
||||
|
||||
_this = _PureComponent.call(this, _props, context) || this;
|
||||
|
||||
_this.handleClose = function () {
|
||||
var addonKey = _this.props.config && _this.props.config.addonKey;
|
||||
var currentAddon = _this.appHelper.addons && _this.appHelper.addons[addonKey];
|
||||
|
||||
if (currentAddon) {
|
||||
_this.utils.transformToPromise(currentAddon.close()).then(function () {
|
||||
_this.setState({
|
||||
dialogVisible: false
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_this.handleOpen = function () {
|
||||
// todo 对话框类型的插件初始时拿不到插件实例
|
||||
_this.setState({
|
||||
dialogVisible: true
|
||||
});
|
||||
};
|
||||
|
||||
_this.handleShow = function () {
|
||||
var _this$props = _this.props,
|
||||
disabled = _this$props.disabled,
|
||||
config = _this$props.config,
|
||||
onClick = _this$props.onClick;
|
||||
var addonKey = config && config.addonKey;
|
||||
if (disabled || !addonKey) return; //考虑到弹窗情况,延时发送消息
|
||||
|
||||
setTimeout(function () {
|
||||
return _this.appHelper.emit(addonKey + ".addon.activate");
|
||||
}, 0);
|
||||
|
||||
_this.handleOpen();
|
||||
|
||||
onClick && onClick();
|
||||
};
|
||||
|
||||
_this.renderIcon = function (clickCallback) {
|
||||
var _this$props2 = _this.props,
|
||||
active = _this$props2.active,
|
||||
disabled = _this$props2.disabled,
|
||||
dotted = _this$props2.dotted,
|
||||
locked = _this$props2.locked,
|
||||
_onClick = _this$props2.onClick,
|
||||
config = _this$props2.config;
|
||||
|
||||
var _ref = config || {},
|
||||
addonKey = _ref.addonKey,
|
||||
props = _ref.props;
|
||||
|
||||
var _ref2 = props || {},
|
||||
icon = _ref2.icon,
|
||||
title = _ref2.title;
|
||||
|
||||
return React.createElement("div", {
|
||||
className: classNames('luna-left-addon', addonKey, {
|
||||
active: active,
|
||||
disabled: disabled,
|
||||
locked: locked
|
||||
}),
|
||||
"data-tooltip": title,
|
||||
onClick: function onClick() {
|
||||
if (disabled) return; //考虑到弹窗情况,延时发送消息
|
||||
|
||||
clickCallback && clickCallback();
|
||||
_onClick && _onClick();
|
||||
}
|
||||
}, dotted ? React.createElement(Badge, {
|
||||
dot: true
|
||||
}, React.createElement(Icon, {
|
||||
type: icon,
|
||||
size: "small"
|
||||
})) : React.createElement(Icon, {
|
||||
type: icon,
|
||||
size: "small"
|
||||
}));
|
||||
};
|
||||
|
||||
_this.state = {
|
||||
dialogVisible: false
|
||||
};
|
||||
_this.appHelper = context.appHelper;
|
||||
_this.utils = _this.appHelper.utils;
|
||||
_this.constants = _this.appHelper.constants;
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = LeftAddon.prototype;
|
||||
|
||||
_proto.componentDidMount = function componentDidMount() {
|
||||
var config = this.props.config;
|
||||
var addonKey = config && config.addonKey;
|
||||
var appHelper = this.appHelper;
|
||||
|
||||
if (appHelper && addonKey) {
|
||||
appHelper.on(addonKey + ".dialog.show", this.handleShow);
|
||||
appHelper.on(addonKey + ".dialog.close", this.handleClose);
|
||||
}
|
||||
};
|
||||
|
||||
_proto.componentWillUnmount = function componentWillUnmount() {
|
||||
var config = this.props.config;
|
||||
var appHelper = this.appHelper;
|
||||
var addonKey = config && config.addonKey;
|
||||
|
||||
if (appHelper && addonKey) {
|
||||
appHelper.off(addonKey + ".dialog.show", this.handleShow);
|
||||
appHelper.off(addonKey + ".dialog.close", this.handleClose);
|
||||
}
|
||||
};
|
||||
|
||||
_proto.render = function render() {
|
||||
var _this2 = this;
|
||||
|
||||
var _this$props3 = this.props,
|
||||
dotted = _this$props3.dotted,
|
||||
locked = _this$props3.locked,
|
||||
active = _this$props3.active,
|
||||
disabled = _this$props3.disabled,
|
||||
config = _this$props3.config;
|
||||
|
||||
var _ref3 = config || {},
|
||||
addonKey = _ref3.addonKey,
|
||||
props = _ref3.props,
|
||||
type = _ref3.type,
|
||||
addonProps = _ref3.addonProps;
|
||||
|
||||
var _ref4 = props || {},
|
||||
_onClick2 = _ref4.onClick,
|
||||
title = _ref4.title;
|
||||
|
||||
var dialogVisible = this.state.dialogVisible;
|
||||
var _this$context = this.context,
|
||||
appHelper = _this$context.appHelper,
|
||||
components = _this$context.components;
|
||||
if (!addonKey || !type || !props) return null;
|
||||
var componentName = appHelper.utils.generateAddonCompName(addonKey);
|
||||
var localeProps = {};
|
||||
var locale = appHelper.locale,
|
||||
messages = appHelper.messages;
|
||||
|
||||
if (locale) {
|
||||
localeProps.locale = locale;
|
||||
}
|
||||
|
||||
if (messages && messages[componentName]) {
|
||||
localeProps.messages = messages[componentName];
|
||||
}
|
||||
|
||||
var AddonComp = components && components[componentName];
|
||||
var node = AddonComp && React.createElement(AddonComp, _extends({
|
||||
active: active,
|
||||
locked: locked,
|
||||
disabled: disabled,
|
||||
config: config,
|
||||
onClick: function onClick() {
|
||||
_onClick2 && _onClick2.call(null, appHelper);
|
||||
}
|
||||
}, localeProps, addonProps || {})) || null;
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return React.createElement("a", props.linkProps || {}, this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, appHelper);
|
||||
}));
|
||||
|
||||
case 'Icon':
|
||||
return this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, appHelper);
|
||||
});
|
||||
|
||||
case 'DialogIcon':
|
||||
return React.createElement(Fragment, null, this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, appHelper);
|
||||
|
||||
_this2.handleOpen();
|
||||
}), React.createElement(Dialog, _extends({
|
||||
onOk: function onOk() {
|
||||
appHelper.emit(addonKey + ".dialog.onOk");
|
||||
|
||||
_this2.handleClose();
|
||||
},
|
||||
onCancel: this.handleClose,
|
||||
onClose: this.handleClose,
|
||||
title: title
|
||||
}, props.dialogProps || {}, {
|
||||
visible: dialogVisible
|
||||
}), node));
|
||||
|
||||
case 'BalloonIcon':
|
||||
return React.createElement(Balloon, _extends({
|
||||
trigger: this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, appHelper);
|
||||
}),
|
||||
align: "r",
|
||||
triggerType: ['click', 'hover']
|
||||
}, props.balloonProps || {}), node);
|
||||
|
||||
case 'PanelIcon':
|
||||
return this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, appHelper);
|
||||
|
||||
_this2.handleOpen();
|
||||
});
|
||||
|
||||
case 'Custom':
|
||||
return dotted ? React.createElement(Badge, {
|
||||
dot: true
|
||||
}, node) : node;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return LeftAddon;
|
||||
}(PureComponent);
|
||||
|
||||
LeftAddon.displayName = 'LunaLeftAddon';
|
||||
LeftAddon.propTypes = {
|
||||
active: PropTypes.bool,
|
||||
config: PropTypes.shape({
|
||||
addonKey: PropTypes.string,
|
||||
addonProps: PropTypes.object,
|
||||
props: PropTypes.object,
|
||||
type: PropTypes.oneOf(['DialogIcon', 'BalloonIcon', 'PanelIcon', 'LinkIcon', 'Icon', 'Custom'])
|
||||
}),
|
||||
disabled: PropTypes.bool,
|
||||
dotted: PropTypes.bool,
|
||||
locked: PropTypes.bool,
|
||||
onClick: PropTypes.func
|
||||
};
|
||||
LeftAddon.defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
locked: false,
|
||||
onClick: function onClick() {}
|
||||
};
|
||||
LeftAddon.contextType = AppContext;
|
||||
export { LeftAddon as default };
|
||||
@ -0,0 +1,59 @@
|
||||
.luna-left-addon {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
height: 36px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: #777;
|
||||
&.collapse {
|
||||
height: 40px;
|
||||
color: #8c8c8c;
|
||||
border-bottom: 1px solid #bfbfbf;
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: #fff !important;
|
||||
background-color: $color-brand1-9 !important;
|
||||
&.disabled {
|
||||
color: #fff;
|
||||
background-color: $color-fill1-7;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&:hover {
|
||||
background-color: $color-brand1-1;
|
||||
color: $color-brand1-6;
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
top: 5px;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
color: #fff;
|
||||
z-index: 100;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 15px;
|
||||
border: 5px solid transparent;
|
||||
border-right-color: rgba(0, 0, 0, 0.75);
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,30 @@
|
||||
/// <reference types="react" />
|
||||
export interface Props {
|
||||
name: string;
|
||||
import { PureComponent } from 'react';
|
||||
import './index.scss';
|
||||
export default class TopIcon extends PureComponent {
|
||||
static displayName: string;
|
||||
static propTypes: {
|
||||
active: any;
|
||||
className: any;
|
||||
disabled: any;
|
||||
icon: any;
|
||||
id: any;
|
||||
locked: any;
|
||||
onClick: any;
|
||||
showTitle: any;
|
||||
style: any;
|
||||
title: any;
|
||||
};
|
||||
static defaultProps: {
|
||||
active: boolean;
|
||||
className: string;
|
||||
disabled: boolean;
|
||||
icon: string;
|
||||
id: string;
|
||||
locked: boolean;
|
||||
onClick: () => void;
|
||||
showTitle: boolean;
|
||||
style: {};
|
||||
title: string;
|
||||
};
|
||||
render(): JSX.Element;
|
||||
}
|
||||
declare const Greeting: ({ name }: Props) => JSX.Element;
|
||||
export default Greeting;
|
||||
|
||||
@ -1,14 +1,76 @@
|
||||
import React from 'react';
|
||||
import _Button from "@alifd/next/es/button";
|
||||
import _Icon from "@alifd/next/es/icon";
|
||||
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import './index.scss';
|
||||
|
||||
var Greeting = function Greeting(_ref) {
|
||||
var name = _ref.name;
|
||||
return React.createElement("div", {
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
fontSize: '40px',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}, "Hello, ", name);
|
||||
var TopIcon = /*#__PURE__*/function (_PureComponent) {
|
||||
_inheritsLoose(TopIcon, _PureComponent);
|
||||
|
||||
function TopIcon() {
|
||||
return _PureComponent.apply(this, arguments) || this;
|
||||
}
|
||||
|
||||
var _proto = TopIcon.prototype;
|
||||
|
||||
_proto.render = function render() {
|
||||
var _this$props = this.props,
|
||||
active = _this$props.active,
|
||||
disabled = _this$props.disabled,
|
||||
icon = _this$props.icon,
|
||||
locked = _this$props.locked,
|
||||
title = _this$props.title,
|
||||
className = _this$props.className,
|
||||
id = _this$props.id,
|
||||
style = _this$props.style,
|
||||
showTitle = _this$props.showTitle,
|
||||
onClick = _this$props.onClick;
|
||||
return React.createElement(_Button, {
|
||||
type: "normal",
|
||||
size: "large",
|
||||
text: true,
|
||||
className: classNames('lowcode-top-btn', className, {
|
||||
active: active,
|
||||
disabled: disabled,
|
||||
locked: locked
|
||||
}),
|
||||
id: id,
|
||||
style: style,
|
||||
onClick: disabled ? null : onClick
|
||||
}, React.createElement("div", null, React.createElement(_Icon, {
|
||||
size: "large",
|
||||
type: icon
|
||||
}), showTitle && React.createElement("span", null, title)));
|
||||
};
|
||||
|
||||
return TopIcon;
|
||||
}(PureComponent);
|
||||
|
||||
TopIcon.displayName = 'TopIcon';
|
||||
TopIcon.propTypes = {
|
||||
active: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
icon: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
locked: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
showTitle: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
title: PropTypes.string
|
||||
};
|
||||
|
||||
export default Greeting;
|
||||
TopIcon.defaultProps = {
|
||||
active: false,
|
||||
className: '',
|
||||
disabled: false,
|
||||
icon: '',
|
||||
id: '',
|
||||
locked: false,
|
||||
onClick: function onClick() {},
|
||||
showTitle: false,
|
||||
style: {},
|
||||
title: ''
|
||||
};
|
||||
export { TopIcon as default };
|
||||
32
packages/editor-skeleton/es/components/TopIcon/index.scss
Normal file
32
packages/editor-skeleton/es/components/TopIcon/index.scss
Normal file
@ -0,0 +1,32 @@
|
||||
.next-btn.next-large.lowcode-top-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
margin: 4px -2px;
|
||||
text-align: center;
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
color: #777;
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
i.next-icon {
|
||||
&:before {
|
||||
font-size: 17px;
|
||||
}
|
||||
margin-right: 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
margin: 0px -5px 0;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
21
packages/editor-skeleton/es/components/TopPlugin/index.d.ts
vendored
Normal file
21
packages/editor-skeleton/es/components/TopPlugin/index.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import { PureComponent } from 'react';
|
||||
import './index.scss';
|
||||
export default class TopPlugin extends PureComponent {
|
||||
static displayName: string;
|
||||
static defaultProps: {
|
||||
active: boolean;
|
||||
config: {};
|
||||
disabled: boolean;
|
||||
dotted: boolean;
|
||||
locked: boolean;
|
||||
onClick: () => void;
|
||||
};
|
||||
constructor(props: any, context: any);
|
||||
componentDidMount(): void;
|
||||
componentWillUnmount(): void;
|
||||
handleShow: () => void;
|
||||
handleClose: () => void;
|
||||
handleOpen: () => void;
|
||||
renderIcon: (clickCallback: any) => JSX.Element;
|
||||
render(): JSX.Element;
|
||||
}
|
||||
@ -0,0 +1,213 @@
|
||||
import _Balloon from "@alifd/next/es/balloon";
|
||||
import _Dialog from "@alifd/next/es/dialog";
|
||||
import _extends from "@babel/runtime/helpers/extends";
|
||||
import _Badge from "@alifd/next/es/badge";
|
||||
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import TopIcon from '../TopIcon';
|
||||
import './index.scss';
|
||||
|
||||
var TopPlugin = /*#__PURE__*/function (_PureComponent) {
|
||||
_inheritsLoose(TopPlugin, _PureComponent);
|
||||
|
||||
function TopPlugin(_props, context) {
|
||||
var _this;
|
||||
|
||||
_this = _PureComponent.call(this, _props, context) || this;
|
||||
|
||||
_this.handleShow = function () {
|
||||
var _this$props = _this.props,
|
||||
disabled = _this$props.disabled,
|
||||
config = _this$props.config,
|
||||
onClick = _this$props.onClick;
|
||||
var addonKey = config && config.addonKey;
|
||||
if (disabled || !addonKey) return; //考虑到弹窗情况,延时发送消息
|
||||
|
||||
setTimeout(function () {
|
||||
return _this.appHelper.emit(addonKey + ".addon.activate");
|
||||
}, 0);
|
||||
|
||||
_this.handleOpen();
|
||||
|
||||
onClick && onClick();
|
||||
};
|
||||
|
||||
_this.handleClose = function () {
|
||||
var addonKey = _this.props.config && _this.props.config.addonKey;
|
||||
var currentAddon = _this.appHelper.addons && _this.appHelper.addons[addonKey];
|
||||
|
||||
if (currentAddon) {
|
||||
_this.utils.transformToPromise(currentAddon.close()).then(function () {
|
||||
_this.setState({
|
||||
dialogVisible: false
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_this.handleOpen = function () {
|
||||
// todo dialog类型的插件初始时拿不动插件实例
|
||||
_this.setState({
|
||||
dialogVisible: true
|
||||
});
|
||||
};
|
||||
|
||||
_this.renderIcon = function (clickCallback) {
|
||||
var _this$props2 = _this.props,
|
||||
active = _this$props2.active,
|
||||
disabled = _this$props2.disabled,
|
||||
dotted = _this$props2.dotted,
|
||||
locked = _this$props2.locked,
|
||||
config = _this$props2.config,
|
||||
_onClick = _this$props2.onClick;
|
||||
|
||||
var _ref = config || {},
|
||||
pluginKey = _ref.pluginKey,
|
||||
props = _ref.props;
|
||||
|
||||
var _ref2 = props || {},
|
||||
icon = _ref2.icon,
|
||||
title = _ref2.title;
|
||||
|
||||
var node = React.createElement(TopIcon, {
|
||||
className: "lowcode-top-addon " + pluginKey,
|
||||
active: active,
|
||||
disabled: disabled,
|
||||
locked: locked,
|
||||
icon: icon,
|
||||
title: title,
|
||||
onClick: function onClick() {
|
||||
if (disabled) return; //考虑到弹窗情况,延时发送消息
|
||||
|
||||
setTimeout(function () {
|
||||
return _this.appHelper.emit(pluginKey + ".addon.activate");
|
||||
}, 0);
|
||||
clickCallback && clickCallback();
|
||||
_onClick && _onClick();
|
||||
}
|
||||
});
|
||||
return dotted ? React.createElement(_Badge, {
|
||||
dot: true
|
||||
}, node) : node;
|
||||
};
|
||||
|
||||
_this.state = {
|
||||
dialogVisible: false
|
||||
};
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = TopPlugin.prototype;
|
||||
|
||||
_proto.componentDidMount = function componentDidMount() {
|
||||
var config = this.props.config;
|
||||
var pluginKey = config && config.pluginKey; // const appHelper = this.appHelper;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.on(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.on(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
};
|
||||
|
||||
_proto.componentWillUnmount = function componentWillUnmount() {// const { config } = this.props;
|
||||
// const addonKey = config && config.addonKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.off(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.off(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
};
|
||||
|
||||
_proto.render = function render() {
|
||||
var _this2 = this;
|
||||
|
||||
var _this$props3 = this.props,
|
||||
active = _this$props3.active,
|
||||
dotted = _this$props3.dotted,
|
||||
locked = _this$props3.locked,
|
||||
disabled = _this$props3.disabled,
|
||||
config = _this$props3.config,
|
||||
editor = _this$props3.editor,
|
||||
Comp = _this$props3.pluginClass;
|
||||
|
||||
var _ref3 = config || {},
|
||||
pluginKey = _ref3.pluginKey,
|
||||
pluginProps = _ref3.pluginProps,
|
||||
props = _ref3.props,
|
||||
type = _ref3.type;
|
||||
|
||||
var _ref4 = props || {},
|
||||
_onClick2 = _ref4.onClick,
|
||||
title = _ref4.title;
|
||||
|
||||
var dialogVisible = this.state.dialogVisible;
|
||||
if (!pluginKey || !type || !Comp) return null;
|
||||
var node = React.createElement(Comp, _extends({
|
||||
active: active,
|
||||
locked: locked,
|
||||
disabled: disabled,
|
||||
config: config,
|
||||
onClick: function onClick() {
|
||||
_onClick2 && _onClick2.call(null, editor);
|
||||
}
|
||||
}, pluginProps));
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return React.createElement("a", props.linkProps, this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, editor);
|
||||
}));
|
||||
|
||||
case 'Icon':
|
||||
return this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, editor);
|
||||
});
|
||||
|
||||
case 'DialogIcon':
|
||||
return React.createElement(Fragment, null, this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, editor);
|
||||
|
||||
_this2.handleOpen();
|
||||
}), React.createElement(_Dialog, _extends({
|
||||
onOk: function onOk() {
|
||||
editor.emit(pluginKey + ".dialog.onOk");
|
||||
|
||||
_this2.handleClose();
|
||||
},
|
||||
onCancel: this.handleClose,
|
||||
onClose: this.handleClose,
|
||||
title: title
|
||||
}, props.dialogProps, {
|
||||
visible: dialogVisible
|
||||
}), node));
|
||||
|
||||
case 'BalloonIcon':
|
||||
return React.createElement(_Balloon, _extends({
|
||||
trigger: this.renderIcon(function () {
|
||||
_onClick2 && _onClick2.call(null, editor);
|
||||
}),
|
||||
triggerType: ['click', 'hover']
|
||||
}, props.balloonProps), node);
|
||||
|
||||
case 'Custom':
|
||||
return dotted ? React.createElement(_Badge, {
|
||||
dot: true
|
||||
}, node) : node;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return TopPlugin;
|
||||
}(PureComponent);
|
||||
|
||||
TopPlugin.displayName = 'lowcodeTopPlugin';
|
||||
TopPlugin.defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
locked: false,
|
||||
onClick: function onClick() {}
|
||||
};
|
||||
export { TopPlugin as default };
|
||||
@ -0,0 +1,2 @@
|
||||
.lowcode-top-addon {
|
||||
}
|
||||
@ -1,3 +1,13 @@
|
||||
body {
|
||||
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma,
|
||||
Arial, PingFang SC-Light, Microsoft YaHei;
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.next-loading {
|
||||
.next-loading-wrap {
|
||||
height: 100%;
|
||||
@ -6,17 +16,17 @@
|
||||
.lowcode-editor {
|
||||
.lowcode-main-content {
|
||||
position: absolute;
|
||||
top: 54px;
|
||||
top: 48px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
.lowcode-center-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f7f7f7;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
3
packages/editor-skeleton/es/index.d.ts
vendored
3
packages/editor-skeleton/es/index.d.ts
vendored
@ -1,5 +1,4 @@
|
||||
/// <reference types="react" />
|
||||
import { PureComponent } from 'react-dom';
|
||||
import { PureComponent } from 'react';
|
||||
import './global.scss';
|
||||
export default class Skeleton extends PureComponent {
|
||||
static displayName: string;
|
||||
|
||||
@ -1,15 +1,29 @@
|
||||
import _ConfigProvider from "@alifd/next/es/config-provider";
|
||||
import _Loading from "@alifd/next/es/loading";
|
||||
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
|
||||
import React, { PureComponent } from 'react-dom'; // import Editor from '@ali/lowcode-engine-editor';
|
||||
import React, { PureComponent } from 'react'; // import Editor from '@ali/lowcode-engine-editor';
|
||||
|
||||
import TopArea from './layouts/TopArea';
|
||||
import LeftArea from './layouts/LeftArea';
|
||||
import CenterArea from './layouts/CenterArea';
|
||||
import RightArea from './layouts/RightArea';
|
||||
import './global.scss';
|
||||
|
||||
var Skeleton = /*#__PURE__*/function (_PureComponent) {
|
||||
_inheritsLoose(Skeleton, _PureComponent);
|
||||
|
||||
function Skeleton(props) {
|
||||
return _PureComponent.call(this, props) || this; // this.editor = new Editor(props.config, props.utils);
|
||||
var _this;
|
||||
|
||||
_this = _PureComponent.call(this, props) || this; // this.editor = new Editor(props.config, props.utils);
|
||||
|
||||
_this.editor = {
|
||||
on: function on() {},
|
||||
off: function off() {},
|
||||
config: props.config,
|
||||
pluginComponents: props.pluginComponents
|
||||
};
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = Skeleton.prototype;
|
||||
@ -23,17 +37,30 @@ var Skeleton = /*#__PURE__*/function (_PureComponent) {
|
||||
location = _this$props.location,
|
||||
history = _this$props.history,
|
||||
messages = _this$props.messages;
|
||||
return React.createElement(_ConfigProvider, {
|
||||
locale: messages[appHelper.locale]
|
||||
}, React.createElement(_Loading, {
|
||||
tip: this.i18n('loading'),
|
||||
this.editor.location = location;
|
||||
this.editor.history = history;
|
||||
this.editor.messages = messages;
|
||||
return React.createElement(_ConfigProvider, null, React.createElement(_Loading, {
|
||||
tip: "Loading",
|
||||
size: "large",
|
||||
visible: loading || !initReady,
|
||||
visible: false,
|
||||
shape: "fusion-reactor",
|
||||
fullScreen: true
|
||||
}, React.createElement("div", {
|
||||
className: "lowcode-editor"
|
||||
})));
|
||||
}, React.createElement(TopArea, {
|
||||
editor: this.editor
|
||||
}), React.createElement("div", {
|
||||
className: "lowcode-main-content"
|
||||
}, React.createElement(LeftArea.Nav, {
|
||||
editor: this.editor
|
||||
}), React.createElement(LeftArea.Panel, {
|
||||
editor: this.editor
|
||||
}), React.createElement(CenterArea, {
|
||||
editor: this.editor
|
||||
}), React.createElement(RightArea, {
|
||||
editor: this.editor
|
||||
})))));
|
||||
};
|
||||
|
||||
return Skeleton;
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.lowcode-center-area {
|
||||
padding: 12px;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
.lowcode-left-area-nav {
|
||||
width: 48px;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-right: 1px solid #e8ebee;
|
||||
position: relative;
|
||||
.top-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
max-height: 100%;
|
||||
}
|
||||
.bottom-area {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
max-height: calc(100% - 20px);
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ var LeftAreaPanel = /*#__PURE__*/function (_PureComponent) {
|
||||
|
||||
_proto.render = function render() {
|
||||
return React.createElement("div", {
|
||||
className: "lowcode-left-area"
|
||||
className: "lowcode-left-area-panel"
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,157 @@
|
||||
.lowcode-right-area {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
border-left: 1px solid #e8ebee;
|
||||
.right-plugin-title {
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9 !important;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
|
||||
//tab定义
|
||||
.next-tabs-wrapped.right-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: -1px;
|
||||
.next-tabs-bar {
|
||||
z-index: 1;
|
||||
}
|
||||
.next-tabs-nav {
|
||||
display: block;
|
||||
.next-tabs-tab {
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
border-right: none !important;
|
||||
margin-right: 0 !important;
|
||||
width: 25%;
|
||||
&.active {
|
||||
background: none;
|
||||
border-bottom-color: #f7f7f7 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.next-tabs-content {
|
||||
flex: 1;
|
||||
.next-tabs-tabpane.active {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
//组件
|
||||
.select-comp {
|
||||
padding: 10px 16px;
|
||||
line-height: 16px;
|
||||
color: #989a9c;
|
||||
& > span {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
& > .btn-wrap,
|
||||
& > .next-btn {
|
||||
width: auto;
|
||||
margin: 0 5px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.unselected {
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
//右侧属性面板样式调整;
|
||||
.offset-56 {
|
||||
padding-left: 56px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.fixedSpan.next-form-item {
|
||||
& > .next-form-item-label {
|
||||
width: 56px;
|
||||
flex: none;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
}
|
||||
}
|
||||
.fixedSpan.next-form-item,
|
||||
.offset-56 .next-form-item {
|
||||
display: flex;
|
||||
& > .next-form-item-control {
|
||||
width: auto;
|
||||
flex: 1;
|
||||
max-width: none;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topSpan.next-form-item {
|
||||
margin-bottom: 4px;
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,5 +3,9 @@ import './index.scss';
|
||||
export default class TopArea extends PureComponent {
|
||||
static displayName: string;
|
||||
constructor(props: any);
|
||||
componentDidMount(): void;
|
||||
componentWillUnmount(): void;
|
||||
handlePluginStatusChange: () => void;
|
||||
renderPluginList: (list?: any[]) => JSX.Element[];
|
||||
render(): JSX.Element;
|
||||
}
|
||||
|
||||
@ -1,20 +1,79 @@
|
||||
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
|
||||
import _Grid from "@alifd/next/es/grid";
|
||||
import React, { PureComponent } from 'react';
|
||||
import TopPlugin from '../../components/TopPlugin';
|
||||
import './index.scss';
|
||||
var Row = _Grid.Row,
|
||||
Col = _Grid.Col;
|
||||
|
||||
var TopArea = /*#__PURE__*/function (_PureComponent) {
|
||||
_inheritsLoose(TopArea, _PureComponent);
|
||||
|
||||
function TopArea(props) {
|
||||
return _PureComponent.call(this, props) || this;
|
||||
var _this;
|
||||
|
||||
_this = _PureComponent.call(this, props) || this;
|
||||
|
||||
_this.handlePluginStatusChange = function () {};
|
||||
|
||||
_this.renderPluginList = function (list) {
|
||||
if (list === void 0) {
|
||||
list = [];
|
||||
}
|
||||
|
||||
return list.map(function (item, idx) {
|
||||
var isDivider = item.type === 'Divider';
|
||||
return React.createElement(Col, {
|
||||
className: isDivider ? 'divider' : '',
|
||||
key: isDivider ? idx : item.pluginKey,
|
||||
style: {
|
||||
width: item.props && item.props.width || 40,
|
||||
flex: 'none'
|
||||
}
|
||||
}, !isDivider && React.createElement(TopPlugin, {
|
||||
config: item,
|
||||
pluginClass: _this.editor.pluginComponents[item.pluginKey],
|
||||
status: _this.editor.pluginStatus[item.pluginKey]
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
_this.editor = props.editor;
|
||||
_this.config = _this.editor.config.plugins && _this.editor.config.plugins.topArea;
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = TopArea.prototype;
|
||||
|
||||
_proto.componentDidMount = function componentDidMount() {};
|
||||
|
||||
_proto.componentWillUnmount = function componentWillUnmount() {};
|
||||
|
||||
_proto.render = function render() {
|
||||
if (!this.config) return null;
|
||||
var leftList = [];
|
||||
var rightList = [];
|
||||
this.config.forEach(function (item) {
|
||||
var align = item.props && item.props.align === 'right' ? 'right' : 'left'; // 分隔符不允许相邻
|
||||
|
||||
if (item.type === 'Divider') {
|
||||
var currentList = align === 'right' ? rightList : leftList;
|
||||
if (currList.length === 0 || currList[currList.length - 1].type === 'Divider') return;
|
||||
}
|
||||
|
||||
if (align === 'right') {
|
||||
rightList.push(item);
|
||||
} else {
|
||||
leftList.push(item);
|
||||
}
|
||||
});
|
||||
return React.createElement("div", {
|
||||
className: "lowcode-top-area"
|
||||
});
|
||||
}, React.createElement("div", {
|
||||
className: "left-area"
|
||||
}, this.renderPluginList(leftList)), React.createElement("div", {
|
||||
classname: "right-area"
|
||||
}, this.renderPluginList(rightList)));
|
||||
};
|
||||
|
||||
return TopArea;
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
.lowcode-top-area {
|
||||
height: 48px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #e8ebee;
|
||||
}
|
||||
@ -1,2 +1,8 @@
|
||||
import '@alifd/next/es/config-provider/style';
|
||||
import '@alifd/next/es/loading/style';
|
||||
import '@alifd/next/es/grid/style';
|
||||
import '@alifd/next/es/balloon/style';
|
||||
import '@alifd/next/es/dialog/style';
|
||||
import '@alifd/next/es/badge/style';
|
||||
import '@alifd/next/es/button/style';
|
||||
import '@alifd/next/es/icon/style';
|
||||
@ -2,6 +2,26 @@
|
||||
"name": "@ali/lowcode-engine-skeleton",
|
||||
"version": "0.0.1",
|
||||
"description": "alibaba lowcode editor skeleton",
|
||||
"files": [
|
||||
"demo/",
|
||||
"es/",
|
||||
"lib/",
|
||||
"build/"
|
||||
],
|
||||
"main": "lib/index.tsx",
|
||||
"module": "es/index.js",
|
||||
"stylePath": "style.js",
|
||||
"scripts": {
|
||||
"start": "build-scripts start",
|
||||
"build": "build-scripts build --skip-demo",
|
||||
"prepublishOnly": "npm run prettier && npm run build",
|
||||
"lint": "eslint --cache --ext .js,.jsx ./",
|
||||
"prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" "
|
||||
},
|
||||
"keywords": [
|
||||
"lowcode",
|
||||
"editor"
|
||||
],
|
||||
"author": "xiayang.xy",
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.x",
|
||||
@ -21,7 +41,7 @@
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/react": "^16.9.13",
|
||||
"@types/react-dom": "^16.9.4",
|
||||
"build-plugin-component": "^0.2.0",
|
||||
"build-plugin-component": "^0.2.7-1",
|
||||
"build-plugin-fusion": "^0.1.0",
|
||||
"build-plugin-moment-locales": "^0.1.0",
|
||||
"eslint": "^6.0.1",
|
||||
@ -29,23 +49,6 @@
|
||||
"react": "^16.8.0",
|
||||
"react-dom": "^16.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "build-scripts start",
|
||||
"build": "build-scripts build",
|
||||
"prepublishOnly": "npm run prettier && npm run build",
|
||||
"lint": "eslint --cache --ext .js,.jsx ./",
|
||||
"prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" "
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"iceworks": {
|
||||
"type": "react",
|
||||
"adapter": "adapter-react-v3"
|
||||
},
|
||||
"ideMode": {
|
||||
"name": "ice-react"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ice-lab/react-materials/tree/master/scaffolds/ice-ts"
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
.luna-left-addon {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
height: 36px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: #777;
|
||||
&.collapse {
|
||||
height: 40px;
|
||||
color: #8c8c8c;
|
||||
border-bottom: 1px solid #bfbfbf;
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: #fff !important;
|
||||
background-color: $color-brand1-9 !important;
|
||||
&.disabled {
|
||||
color: #fff;
|
||||
background-color: $color-fill1-7;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&:hover {
|
||||
background-color: $color-brand1-1;
|
||||
color: $color-brand1-6;
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
top: 5px;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
color: #fff;
|
||||
z-index: 100;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 15px;
|
||||
border: 5px solid transparent;
|
||||
border-right-color: rgba(0, 0, 0, 0.75);
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,223 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import AppContext from '@ali/iceluna-sdk/lib/context/appContext';
|
||||
import { Balloon, Dialog, Icon, Badge } from '@alife/next';
|
||||
|
||||
import './index.scss';
|
||||
export default class LeftAddon extends PureComponent {
|
||||
static displayName = 'LunaLeftAddon';
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
config: PropTypes.shape({
|
||||
addonKey: PropTypes.string,
|
||||
addonProps: PropTypes.object,
|
||||
props: PropTypes.object,
|
||||
type: PropTypes.oneOf([
|
||||
'DialogIcon',
|
||||
'BalloonIcon',
|
||||
'PanelIcon',
|
||||
'LinkIcon',
|
||||
'Icon',
|
||||
'Custom',
|
||||
]),
|
||||
}),
|
||||
disabled: PropTypes.bool,
|
||||
dotted: PropTypes.bool,
|
||||
locked: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
};
|
||||
static contextType = AppContext;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
dialogVisible: false,
|
||||
};
|
||||
this.appHelper = context.appHelper;
|
||||
this.utils = this.appHelper.utils;
|
||||
this.constants = this.appHelper.constants;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { config } = this.props;
|
||||
const addonKey = config && config.addonKey;
|
||||
const appHelper = this.appHelper;
|
||||
if (appHelper && addonKey) {
|
||||
appHelper.on(`${addonKey}.dialog.show`, this.handleShow);
|
||||
appHelper.on(`${addonKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { config } = this.props;
|
||||
const appHelper = this.appHelper;
|
||||
const addonKey = config && config.addonKey;
|
||||
if (appHelper && addonKey) {
|
||||
appHelper.off(`${addonKey}.dialog.show`, this.handleShow);
|
||||
appHelper.off(`${addonKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
const addonKey = this.props.config && this.props.config.addonKey;
|
||||
const currentAddon =
|
||||
this.appHelper.addons && this.appHelper.addons[addonKey];
|
||||
if (currentAddon) {
|
||||
this.utils.transformToPromise(currentAddon.close()).then(() => {
|
||||
this.setState({
|
||||
dialogVisible: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
// todo 对话框类型的插件初始时拿不到插件实例
|
||||
this.setState({
|
||||
dialogVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleShow = () => {
|
||||
const { disabled, config, onClick } = this.props;
|
||||
const addonKey = config && config.addonKey;
|
||||
if (disabled || !addonKey) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0);
|
||||
this.handleOpen();
|
||||
onClick && onClick();
|
||||
};
|
||||
|
||||
renderIcon = clickCallback => {
|
||||
const { active, disabled, dotted, locked, onClick, config } = this.props;
|
||||
const { addonKey, props } = config || {};
|
||||
const { icon, title } = props || {};
|
||||
return (
|
||||
<div
|
||||
className={classNames('luna-left-addon', addonKey, {
|
||||
active,
|
||||
disabled,
|
||||
locked,
|
||||
})}
|
||||
data-tooltip={title}
|
||||
onClick={() => {
|
||||
if (disabled) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
clickCallback && clickCallback();
|
||||
onClick && onClick();
|
||||
}}
|
||||
>
|
||||
{dotted ? (
|
||||
<Badge dot>
|
||||
<Icon type={icon} size="small" />
|
||||
</Badge>
|
||||
) : (
|
||||
<Icon type={icon} size="small" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { dotted, locked, active, disabled, config } = this.props;
|
||||
const { addonKey, props, type, addonProps } = config || {};
|
||||
const { onClick, title } = props || {};
|
||||
const { dialogVisible } = this.state;
|
||||
const { appHelper, components } = this.context;
|
||||
if (!addonKey || !type || !props) return null;
|
||||
const componentName = appHelper.utils.generateAddonCompName(addonKey);
|
||||
const localeProps = {};
|
||||
const { locale, messages } = appHelper;
|
||||
if (locale) {
|
||||
localeProps.locale = locale;
|
||||
}
|
||||
if (messages && messages[componentName]) {
|
||||
localeProps.messages = messages[componentName];
|
||||
}
|
||||
const AddonComp = components && components[componentName];
|
||||
const node =
|
||||
(AddonComp && (
|
||||
<AddonComp
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
}}
|
||||
{...localeProps}
|
||||
{...(addonProps || {})}
|
||||
/>
|
||||
)) ||
|
||||
null;
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return (
|
||||
<a {...(props.linkProps || {})}>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
})}
|
||||
</a>
|
||||
);
|
||||
case 'Icon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
});
|
||||
case 'DialogIcon':
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
this.handleOpen();
|
||||
})}
|
||||
<Dialog
|
||||
onOk={() => {
|
||||
appHelper.emit(`${addonKey}.dialog.onOk`);
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={this.handleClose}
|
||||
onClose={this.handleClose}
|
||||
title={title}
|
||||
{...(props.dialogProps || {})}
|
||||
visible={dialogVisible}
|
||||
>
|
||||
{node}
|
||||
</Dialog>
|
||||
</Fragment>
|
||||
);
|
||||
case 'BalloonIcon':
|
||||
return (
|
||||
<Balloon
|
||||
trigger={this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
})}
|
||||
align="r"
|
||||
triggerType={['click', 'hover']}
|
||||
{...(props.balloonProps || {})}
|
||||
>
|
||||
{node}
|
||||
</Balloon>
|
||||
);
|
||||
case 'PanelIcon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
this.handleOpen();
|
||||
});
|
||||
case 'Custom':
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
packages/editor-skeleton/src/components/TopIcon/index.scss
Normal file
32
packages/editor-skeleton/src/components/TopIcon/index.scss
Normal file
@ -0,0 +1,32 @@
|
||||
.next-btn.next-large.lowcode-top-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
margin: 4px -2px;
|
||||
text-align: center;
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
color: #777;
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
i.next-icon {
|
||||
&:before {
|
||||
font-size: 17px;
|
||||
}
|
||||
margin-right: 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
margin: 0px -5px 0;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,68 @@
|
||||
import React from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
export interface Props {
|
||||
name: string;
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Icon, Button } from '@alifd/next';
|
||||
import './index.scss';
|
||||
export default class TopIcon extends PureComponent {
|
||||
static displayName = 'TopIcon';
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
icon: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
locked: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
showTitle: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
className: '',
|
||||
disabled: false,
|
||||
icon: '',
|
||||
id: '',
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
showTitle: false,
|
||||
style: {},
|
||||
title: '',
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
active,
|
||||
disabled,
|
||||
icon,
|
||||
locked,
|
||||
title,
|
||||
className,
|
||||
id,
|
||||
style,
|
||||
showTitle,
|
||||
onClick,
|
||||
} = this.props;
|
||||
return (
|
||||
<Button
|
||||
type="normal"
|
||||
size="large"
|
||||
text={true}
|
||||
className={classNames('lowcode-top-btn', className, {
|
||||
active,
|
||||
disabled,
|
||||
locked,
|
||||
})}
|
||||
id={id}
|
||||
style={style}
|
||||
onClick={disabled ? null : onClick}
|
||||
>
|
||||
<div>
|
||||
<Icon size="large" type={icon} />
|
||||
{showTitle && <span>{title}</span>}
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Greeting = ({ name }: Props) => {
|
||||
return (
|
||||
<div style={{ textAlign: 'center', fontSize: '40px', fontWeight: 'bold' }}>
|
||||
Hello, {name}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Greeting;
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
.lowcode-top-addon {
|
||||
}
|
||||
@ -0,0 +1,174 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import TopIcon from '../TopIcon';
|
||||
import { Balloon, Badge, Dialog } from '@alifd/next';
|
||||
|
||||
import './index.scss';
|
||||
export default class TopPlugin extends PureComponent {
|
||||
static displayName = 'lowcodeTopPlugin';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
dialogVisible: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { config } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.on(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.on(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// const { config } = this.props;
|
||||
// const addonKey = config && config.addonKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.off(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.off(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
}
|
||||
|
||||
handleShow = () => {
|
||||
const { disabled, config, onClick } = this.props;
|
||||
const addonKey = config && config.addonKey;
|
||||
if (disabled || !addonKey) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0);
|
||||
this.handleOpen();
|
||||
onClick && onClick();
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
const addonKey = this.props.config && this.props.config.addonKey;
|
||||
const currentAddon =
|
||||
this.appHelper.addons && this.appHelper.addons[addonKey];
|
||||
if (currentAddon) {
|
||||
this.utils.transformToPromise(currentAddon.close()).then(() => {
|
||||
this.setState({
|
||||
dialogVisible: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
// todo dialog类型的插件初始时拿不动插件实例
|
||||
this.setState({
|
||||
dialogVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
renderIcon = clickCallback => {
|
||||
const { active, disabled, dotted, locked, config, onClick } = this.props;
|
||||
const { pluginKey, props } = config || {};
|
||||
const { icon, title } = props || {};
|
||||
const node = (
|
||||
<TopIcon
|
||||
className={`lowcode-top-addon ${pluginKey}`}
|
||||
active={active}
|
||||
disabled={disabled}
|
||||
locked={locked}
|
||||
icon={icon}
|
||||
title={title}
|
||||
onClick={() => {
|
||||
if (disabled) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
setTimeout(
|
||||
() => this.appHelper.emit(`${pluginKey}.addon.activate`),
|
||||
0,
|
||||
);
|
||||
clickCallback && clickCallback();
|
||||
onClick && onClick();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { active, dotted, locked, disabled, config, editor, pluginClass: Comp } = this.props;
|
||||
const { pluginKey, pluginProps, props, type } = config || {};
|
||||
const { onClick, title } = props || {};
|
||||
const { dialogVisible } = this.state;
|
||||
if (!pluginKey || !type || !Comp) return null;
|
||||
const node = <Comp
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
}}
|
||||
{...pluginProps}
|
||||
/>;
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return (
|
||||
<a {...props.linkProps}>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
</a>
|
||||
);
|
||||
case 'Icon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
});
|
||||
case 'DialogIcon':
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
})}
|
||||
<Dialog
|
||||
onOk={() => {
|
||||
editor.emit(`${pluginKey}.dialog.onOk`);
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={this.handleClose}
|
||||
onClose={this.handleClose}
|
||||
title={title}
|
||||
{...props.dialogProps}
|
||||
visible={dialogVisible}
|
||||
>
|
||||
{node}
|
||||
</Dialog>
|
||||
</Fragment>
|
||||
);
|
||||
case 'BalloonIcon':
|
||||
return (
|
||||
<Balloon
|
||||
trigger={this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
triggerType={['click', 'hover']}
|
||||
{...props.balloonProps}
|
||||
>
|
||||
{node}
|
||||
</Balloon>
|
||||
);
|
||||
case 'Custom':
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ const routerConfig = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
body {
|
||||
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma,
|
||||
Arial, PingFang SC-Light, Microsoft YaHei;
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.next-loading {
|
||||
.next-loading-wrap {
|
||||
height: 100%;
|
||||
@ -6,17 +16,17 @@
|
||||
.lowcode-editor {
|
||||
.lowcode-main-content {
|
||||
position: absolute;
|
||||
top: 54px;
|
||||
top: 48px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
.lowcode-center-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f7f7f7;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, {PureComponent} from 'react-dom';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// import Editor from '@ali/lowcode-engine-editor';
|
||||
import { Loading, ConfigProvider } from '@alifd/next';
|
||||
@ -17,6 +17,12 @@ export default class Skeleton extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// this.editor = new Editor(props.config, props.utils);
|
||||
this.editor = {
|
||||
on: () => {},
|
||||
off: () => {},
|
||||
config: props.config,
|
||||
pluginComponents: props.pluginComponents
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -26,24 +32,26 @@ export default class Skeleton extends PureComponent {
|
||||
|
||||
render() {
|
||||
const { location, history, messages } = this.props;
|
||||
|
||||
this.editor.location = location;
|
||||
this.editor.history = history;
|
||||
this.editor.messages = messages;
|
||||
return (
|
||||
<ConfigProvider locale={messages[appHelper.locale]}>
|
||||
<ConfigProvider>
|
||||
<Loading
|
||||
tip={this.i18n('loading')}
|
||||
tip="Loading"
|
||||
size="large"
|
||||
visible={loading || !initReady}
|
||||
visible={false}
|
||||
shape="fusion-reactor"
|
||||
fullScreen
|
||||
>
|
||||
<div className="lowcode-editor">
|
||||
{/* <TopArea/>
|
||||
<TopArea editor={this.editor}/>
|
||||
<div className="lowcode-main-content">
|
||||
<LeftArea.Nav/>
|
||||
<LeftArea.Panel/>
|
||||
<CenterArea/>
|
||||
<RightArea/>
|
||||
</div> */}
|
||||
<LeftArea.Nav editor={this.editor}/>
|
||||
<LeftArea.Panel editor={this.editor}/>
|
||||
<CenterArea editor={this.editor}/>
|
||||
<RightArea editor={this.editor}/>
|
||||
</div>
|
||||
</div>
|
||||
</Loading>
|
||||
</ConfigProvider>
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.lowcode-center-area {
|
||||
padding: 12px;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@ -10,8 +10,6 @@ export default class CenterArea extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="lowcode-center-area" />
|
||||
);
|
||||
return <div className="lowcode-center-area"></div>;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
.lowcode-left-area-nav {
|
||||
width: 48px;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-right: 1px solid #e8ebee;
|
||||
position: relative;
|
||||
.top-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
max-height: 100%;
|
||||
}
|
||||
.bottom-area {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
max-height: calc(100% - 20px);
|
||||
}
|
||||
}
|
||||
@ -3,5 +3,5 @@ import Panel from './panel';
|
||||
|
||||
export default {
|
||||
Nav,
|
||||
Panel
|
||||
Panel,
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@ -10,8 +10,6 @@ export default class LeftAreaPanel extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="lowcode-left-area-nav"/>
|
||||
);
|
||||
return <div className="lowcode-left-area-nav" />;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@ -10,8 +10,6 @@ export default class LeftAreaPanel extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="lowcode-left-area"/>
|
||||
);
|
||||
return <div className="lowcode-left-area-panel" />;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
.lowcode-right-area {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
border-left: 1px solid #e8ebee;
|
||||
.right-plugin-title {
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9 !important;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
|
||||
//tab定义
|
||||
.next-tabs-wrapped.right-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: -1px;
|
||||
.next-tabs-bar {
|
||||
z-index: 1;
|
||||
}
|
||||
.next-tabs-nav {
|
||||
display: block;
|
||||
.next-tabs-tab {
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
border-right: none !important;
|
||||
margin-right: 0 !important;
|
||||
width: 25%;
|
||||
&.active {
|
||||
background: none;
|
||||
border-bottom-color: #f7f7f7 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.next-tabs-content {
|
||||
flex: 1;
|
||||
.next-tabs-tabpane.active {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
//组件
|
||||
.select-comp {
|
||||
padding: 10px 16px;
|
||||
line-height: 16px;
|
||||
color: #989a9c;
|
||||
& > span {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
& > .btn-wrap,
|
||||
& > .next-btn {
|
||||
width: auto;
|
||||
margin: 0 5px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.unselected {
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
//右侧属性面板样式调整;
|
||||
.offset-56 {
|
||||
padding-left: 56px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.fixedSpan.next-form-item {
|
||||
& > .next-form-item-label {
|
||||
width: 56px;
|
||||
flex: none;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
}
|
||||
}
|
||||
.fixedSpan.next-form-item,
|
||||
.offset-56 .next-form-item {
|
||||
display: flex;
|
||||
& > .next-form-item-control {
|
||||
width: auto;
|
||||
flex: 1;
|
||||
max-width: none;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topSpan.next-form-item {
|
||||
margin-bottom: 4px;
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@ -10,8 +10,6 @@ export default class RightArea extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="lowcode-right-area"/>
|
||||
);
|
||||
return <div className="lowcode-right-area" />;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
.lowcode-top-area {
|
||||
height: 48px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #e8ebee;
|
||||
}
|
||||
@ -1,17 +1,79 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Grid } from '@alifd/next';
|
||||
import TopPlugin from '../../components/TopPlugin';
|
||||
import './index.scss';
|
||||
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
export default class TopArea extends PureComponent {
|
||||
static displayName = 'lowcodeTopArea';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.config = this.editor.config.plugins && this.editor.config.plugins.topArea;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
}
|
||||
componentWillUnmount() {
|
||||
}
|
||||
|
||||
handlePluginStatusChange = () => {};
|
||||
|
||||
renderPluginList = (list = []) => {
|
||||
return list.map((item, idx) => {
|
||||
const isDivider = item.type === 'Divider';
|
||||
|
||||
return (
|
||||
<Col
|
||||
className={isDivider ? 'divider' : ''}
|
||||
key={isDivider ? idx : item.pluginKey}
|
||||
style={{
|
||||
width: (item.props && item.props.width) || 40,
|
||||
flex: 'none',
|
||||
}}
|
||||
>
|
||||
{!isDivider && (
|
||||
<TopPlugin
|
||||
config={item}
|
||||
pluginClass={this.editor.pluginComponents[item.pluginKey]}
|
||||
status={this.editor.pluginStatus[item.pluginKey]}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.config) return null;
|
||||
const leftList = [];
|
||||
const rightList = [];
|
||||
this.config.forEach(item => {
|
||||
const align =
|
||||
item.props && item.props.align === 'right' ? 'right' : 'left';
|
||||
// 分隔符不允许相邻
|
||||
if (item.type === 'Divider') {
|
||||
const currentList = align === 'right' ? rightList : leftList;
|
||||
if (
|
||||
currList.length === 0 ||
|
||||
currList[currList.length - 1].type === 'Divider'
|
||||
)
|
||||
return;
|
||||
}
|
||||
if (align === 'right') {
|
||||
rightList.push(item);
|
||||
} else {
|
||||
leftList.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="lowcode-top-area"/>
|
||||
<div className="lowcode-top-area">
|
||||
<div className="left-area">{this.renderPluginList(leftList)}</div>
|
||||
<div classname="right-area">{this.renderPluginList(rightList)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -6,5 +6,5 @@ export default {
|
||||
pageNotExist: 'The current Page not exist',
|
||||
enterFromAppCenter: 'Please enter from the app center',
|
||||
noPermission: 'Sorry, you do not have the develop permission',
|
||||
getPermission: 'Please connect the app owners {owners} to get the permission'
|
||||
getPermission: 'Please connect the app owners {owners} to get the permission',
|
||||
};
|
||||
|
||||
@ -6,5 +6,5 @@ export default {
|
||||
pageNotExist: '当前访问地址不存在',
|
||||
enterFromAppCenter: '请从应用中心入口重新进入',
|
||||
noPermission: '抱歉,您暂无开发权限',
|
||||
getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限'
|
||||
getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限',
|
||||
};
|
||||
|
||||
@ -8,7 +8,7 @@ module.exports = {
|
||||
},
|
||||
plugins: [
|
||||
['ice-plugin-fusion', {
|
||||
themePackage: '@icedesign/theme',
|
||||
themePackage: '@alife/dpl-iceluna',
|
||||
}],
|
||||
['ice-plugin-moment-locales', {
|
||||
locales: ['zh-cn'],
|
||||
|
||||
@ -4,9 +4,11 @@
|
||||
"description": "低代码编辑器",
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.x",
|
||||
"@alife/dpl-iceluna": "^2.3.2",
|
||||
"@icedesign/theme": "^1.x",
|
||||
"@types/react": "^16.8.3",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
"keymaster": "^1.6.2",
|
||||
"moment": "^2.23.0",
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^16.4.1",
|
||||
@ -20,6 +22,7 @@
|
||||
"ice-plugin-fusion": "^0.1.4",
|
||||
"ice-plugin-moment-locales": "^0.1.0",
|
||||
"ice-scripts": "^2.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
"stylelint": "^10.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
@ -27,7 +30,8 @@
|
||||
"build": "ice-scripts build",
|
||||
"lint": "npm run eslint && npm run stylelint",
|
||||
"eslint": "eslint --cache --ext .js,.jsx ./",
|
||||
"stylelint": "stylelint ./**/*.scss"
|
||||
"stylelint": "stylelint ./**/*.scss",
|
||||
"prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" "
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
|
||||
@ -1,3 +1,24 @@
|
||||
|
||||
import topBalloonIcon from '@ali/iceluna-addon-2';
|
||||
import topDialogIcon from '@ali/iceluna-addon-2';
|
||||
import leftPanelIcon from '@ali/iceluna-addon-2';
|
||||
import leftBalloonIcon from '@ali/iceluna-addon-2';
|
||||
import leftDialogIcon from '@ali/iceluna-addon-2';
|
||||
import rightPanel1 from '@ali/iceluna-addon-2';
|
||||
import rightPanel2 from '@ali/iceluna-addon-2';
|
||||
import rightPanel3 from '@ali/iceluna-addon-2';
|
||||
import rightPanel4 from '@ali/iceluna-addon-2';
|
||||
|
||||
import PluginFactory from '../framework/plugin';
|
||||
|
||||
export default {
|
||||
topBalloonIcon: PluginFactory(topBalloonIcon),
|
||||
topDialogIcon: PluginFactory(topDialogIcon),
|
||||
leftPanelIcon: PluginFactory(leftPanelIcon),
|
||||
leftBalloonIcon:PluginFactory(leftBalloonIcon),
|
||||
leftDialogIcon:PluginFactory(leftDialogIcon),
|
||||
rightPanel1:PluginFactory(rightPanel1),
|
||||
rightPanel2:PluginFactory(rightPanel2),
|
||||
rightPanel3: PluginFactory(rightPanel3),
|
||||
rightPanel4: PluginFactory(rightPanel4),
|
||||
};
|
||||
@ -1,3 +1,3 @@
|
||||
export default {
|
||||
"namespace": "page"
|
||||
}
|
||||
namespace: 'page',
|
||||
};
|
||||
|
||||
@ -6,5 +6,5 @@ export default {
|
||||
'en-US': en_us,
|
||||
'zh-CN': zh_cn,
|
||||
'zh-TW': zh_tw,
|
||||
'ja-JP': ja_jp
|
||||
'ja-JP': ja_jp,
|
||||
};
|
||||
@ -1,435 +1,215 @@
|
||||
export default {
|
||||
"skeleton": {
|
||||
"config": {
|
||||
"package": "@ali/lowcode-skeleton",
|
||||
"version": "0.0.1"
|
||||
version: '^1.0.2',
|
||||
theme: {
|
||||
dpl: {
|
||||
package: '@alife/dpl-iceluna',
|
||||
version: '^2.3.0',
|
||||
},
|
||||
scss: '',
|
||||
},
|
||||
"theme": {
|
||||
"fusion": {
|
||||
"package": "@alife/dpl-iceluna",
|
||||
"version": "^2.3.0"
|
||||
},
|
||||
"scss": ""
|
||||
constants: {
|
||||
namespace: 'page',
|
||||
},
|
||||
"constants": {
|
||||
"namespace": "page"
|
||||
utils: [],
|
||||
plugins: {
|
||||
topArea: [
|
||||
{
|
||||
pluginKey: 'topBalloonIcon',
|
||||
type: 'BalloonIcon',
|
||||
props: {
|
||||
align: 'left',
|
||||
title: 'balloon',
|
||||
icon: 'dengpao',
|
||||
balloonProps: {
|
||||
triggerType: 'click',
|
||||
},
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'divider',
|
||||
type: 'Divider',
|
||||
props: {
|
||||
align: 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginKey: 'topDialogIcon',
|
||||
type: 'DialogIcon',
|
||||
props: {
|
||||
align: 'left',
|
||||
title: 'dialog',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'topLinkIcon',
|
||||
type: 'LinkIcon',
|
||||
props: {
|
||||
align: 'right',
|
||||
title: 'link',
|
||||
icon: 'dengpao',
|
||||
linkProps: {
|
||||
href: '//www.taobao.com',
|
||||
target: 'blank',
|
||||
},
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'topIcon',
|
||||
type: 'Icon',
|
||||
props: {
|
||||
align: 'right',
|
||||
title: 'icon',
|
||||
icon: 'dengpao',
|
||||
onClick: function(editor) {
|
||||
alert('icon addon invoke, current activeKey: ' + editor.activeKey);
|
||||
},
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {},
|
||||
},
|
||||
],
|
||||
leftArea: [
|
||||
{
|
||||
pluginKey: 'leftPanelIcon',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
title: 'panel',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftBalloonIcon',
|
||||
type: 'BalloonIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
title: 'balloon',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftDialogIcon',
|
||||
type: 'DialogIcon',
|
||||
props: {
|
||||
align: 'bottom',
|
||||
title: 'dialog',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftLinkIcon',
|
||||
type: 'LinkIcon',
|
||||
props: {
|
||||
align: 'bottom',
|
||||
title: 'link',
|
||||
icon: 'dengpao',
|
||||
linkProps: {
|
||||
href: '//www.taobao.com',
|
||||
target: 'blank',
|
||||
},
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftIcon',
|
||||
type: 'Icon',
|
||||
props: {
|
||||
align: 'bottom',
|
||||
title: 'icon',
|
||||
icon: 'dengpao',
|
||||
onClick: function(editor) {
|
||||
alert('icon addon invoke, current activeKey: ' + editor.activeKey);
|
||||
},
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {},
|
||||
},
|
||||
],
|
||||
rightArea: [
|
||||
{
|
||||
pluginKey: 'rightPanel1',
|
||||
type: 'Panel',
|
||||
props: {
|
||||
title: 'panel1',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'rightPanel2',
|
||||
type: 'Panel',
|
||||
props: {
|
||||
title: 'panel2',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'rightPanel3',
|
||||
type: 'Panel',
|
||||
props: {
|
||||
title: 'panel3',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
{
|
||||
pluginKey: 'rightPanel4',
|
||||
type: 'Panel',
|
||||
props: {
|
||||
title: 'panel4',
|
||||
icon: 'dengpao',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
],
|
||||
centerArea: [],
|
||||
},
|
||||
"utils": [],
|
||||
"plugins": {
|
||||
"topArea": [{
|
||||
"pluginKey": "logo",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"width": 110,
|
||||
"align": "left"
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-logo",
|
||||
"version": "^1.0.2"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "divider",
|
||||
"type": "Divider",
|
||||
"props": {
|
||||
"align": "left"
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "pageList",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "left",
|
||||
"width": 360
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-page-list",
|
||||
"version": "^1.0.11"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "partner",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"width": 200
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-partner",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "divider",
|
||||
"type": "Divider",
|
||||
"props": {
|
||||
"align": "right"
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "designMode",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"width": 144
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-design-mode",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "divider",
|
||||
"type": "Divider",
|
||||
"props": {
|
||||
"align": "right"
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "undoRedo",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"width": 88
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-undo-redo",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "d2c",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"width": 44
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-d2c",
|
||||
"version": "^1.0.1"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "history",
|
||||
"type": "DialogIcon",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"icon": "lishijilu1",
|
||||
"title": "历史",
|
||||
"dialogProps": {
|
||||
"title": "历史记录",
|
||||
"footer": false,
|
||||
"shouldUpdatePosition": true
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-history",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "refresh",
|
||||
"type": "Icon",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"icon": "shuaxin",
|
||||
"title": "刷新",
|
||||
"onClick": function(appHelper) {
|
||||
appHelper.emit('ide.reset');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "divider",
|
||||
"type": "Divider",
|
||||
"props": {
|
||||
"align": "right"
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "save",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"width": 86
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-save",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "preview",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"width": 86
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-preview",
|
||||
"version": "^1.0.1"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "publish",
|
||||
"type": "Custom",
|
||||
"props": {
|
||||
"align": "right",
|
||||
"width": 104
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-publish",
|
||||
"version": "^1.0.1"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}],
|
||||
"leftArea": [{
|
||||
"pluginKey": "componentTree",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "top",
|
||||
"icon": "shuxingkongjian",
|
||||
"title": "组件树",
|
||||
"panelProps": {
|
||||
"minWidth": 100,
|
||||
"maxWidth": 500
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-component-tree",
|
||||
"version": "^1.0.5"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "componentList",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "top",
|
||||
"icon": "zujianku",
|
||||
"title": "组件库"
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-component-list",
|
||||
"version": "^1.0.4"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "blockList",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "top",
|
||||
"icon": "jihe",
|
||||
"title": "区块库"
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-block-list",
|
||||
"version": "^1.0.2"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "schema",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "ceshi",
|
||||
"title": "schema 源码开发",
|
||||
"panelProps": {
|
||||
"defaultWidth": 480
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-schema",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "style",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "SCSS",
|
||||
"title": "scss 全局样式设置",
|
||||
"panelProps": {
|
||||
"defaultWidth": 480
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-style",
|
||||
"version": "^1.0.2"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "utils",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "funcsgaiban",
|
||||
"title": "utils 全局公共函数设置",
|
||||
"panelProps": {
|
||||
"defaultWidth": 540
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-utils",
|
||||
"version": "^1.0.7"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "constants",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "constgaiban",
|
||||
"title": "constants 全局常量设置",
|
||||
"panelProps": {
|
||||
"defaultWidth": 480
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-constants",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "package",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "packagegaiban",
|
||||
"title": "package.json 应用设置",
|
||||
"panelProps": {
|
||||
"defaultWidth": 480
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-package",
|
||||
"version": "^1.0.2"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "canvasSetting",
|
||||
"type": "PanelIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "huabushezhi",
|
||||
"title": "canvas 画布配置",
|
||||
"panelProps": {
|
||||
"defaultWidth": 300
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-canvas-setting",
|
||||
"version": "^1.0.2"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "issue",
|
||||
"type": "LinkIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "chongzi",
|
||||
"title": "issue 问题反馈",
|
||||
"linkProps": {
|
||||
"href": "//work.aone.alibaba-inc.com/project/860698/issue/new",
|
||||
"target": "blank"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "document",
|
||||
"type": "LinkIcon",
|
||||
"props": {
|
||||
"align": "bottom",
|
||||
"icon": "wendangzhongxin",
|
||||
"title": "docs 文档中心",
|
||||
"linkProps": {
|
||||
"href": "https://iceluna.alibaba-inc.com/#/document",
|
||||
"target": "blank"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"rightArea": [{
|
||||
"pluginKey": "componentStyle",
|
||||
"props": {
|
||||
"title": "样式"
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-component-style",
|
||||
"version": "^1.0.8"
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "componentAttr",
|
||||
"props": {
|
||||
"title": "属性"
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-component-attr",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "componentEvent",
|
||||
"props": {
|
||||
"title": "事件"
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-component-event",
|
||||
"version": "^1.0.4"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}, {
|
||||
"pluginKey": "componentData",
|
||||
"props": {
|
||||
"title": "数据"
|
||||
},
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-component-data",
|
||||
"version": "^1.0.3"
|
||||
},
|
||||
"pluginProps": {}
|
||||
}],
|
||||
"centerArea": [{
|
||||
"pluginKey": "canvas",
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-canvas",
|
||||
"version": "^1.0.8"
|
||||
}
|
||||
}, {
|
||||
"pluginKey": "guide",
|
||||
"config": {
|
||||
"package": "@ali/iceluna-addon-guide",
|
||||
"version": "^1.0.1"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"hooks": [{
|
||||
"message": "wsHelper.result.updateInfo",
|
||||
"type": "on",
|
||||
"handler": function(appHelper, data) {
|
||||
const pageInfo = appHelper.pageInfo;
|
||||
if (data && data.code > 0 && pageInfo) {
|
||||
const {
|
||||
clientLocks,
|
||||
entityLocks,
|
||||
entityUsers,
|
||||
entityPubInfo
|
||||
} = data.data;
|
||||
if (JSON.stringify(clientLocks || {}) !== JSON.stringify(appHelper.clientLocks || {})) {
|
||||
clientLocks.schema = clientLocks[pageInfo.id];
|
||||
appHelper.set('clientLocks', clientLocks);
|
||||
appHelper.emit('wsHelper.update.clientLocks', clientLocks);
|
||||
}
|
||||
if (JSON.stringify(entityLocks || {}) !== JSON.stringify(appHelper.entityLocks || {})) {
|
||||
entityLocks.schema = entityLocks[pageInfo.id];
|
||||
appHelper.set('entityLocks', entityLocks);
|
||||
appHelper.emit('wsHelper.update.entityLocks', entityLocks);
|
||||
}
|
||||
if (JSON.stringify(entityUsers || {}) !== JSON.stringify(appHelper.entityUsers || {})) {
|
||||
appHelper.set('entityUsers', entityUsers);
|
||||
appHelper.emit('wsHelper.update.entityUsers', entityUsers);
|
||||
}
|
||||
if (JSON.stringify(entityPubInfo || {}) !== JSON.stringify(appHelper.entityPubInfo || {})) {
|
||||
appHelper.set('entityPubInfo', entityPubInfo);
|
||||
appHelper.emit('wsHelper.update.entityPubInfo', entityPubInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
"shortCuts": [],
|
||||
"lifeCycles": {}
|
||||
hooks: [],
|
||||
shortCuts: [],
|
||||
};
|
||||
@ -1,3 +1 @@
|
||||
export default {
|
||||
|
||||
};
|
||||
export default {};
|
||||
|
||||
3
packages/editor/src/framework/context.ts
Normal file
3
packages/editor/src/framework/context.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { createContext } from 'react';
|
||||
const context = createContext({});
|
||||
export default context;
|
||||
41
packages/editor/src/framework/definitions.ts
Normal file
41
packages/editor/src/framework/definitions.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export interface EditorConfig {}
|
||||
|
||||
export interface NpmConfig {
|
||||
version: string;
|
||||
package: string;
|
||||
main?: string;
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
destructuring?: boolean;
|
||||
}
|
||||
|
||||
export interface SkeletonConfig {
|
||||
config: NpmConfig;
|
||||
props?: object;
|
||||
handler?: (EditorConfig) => EditorConfig;
|
||||
}
|
||||
|
||||
export interface FusionTheme {
|
||||
package: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface ThemeConfig {
|
||||
fusion?: FusionTheme;
|
||||
}
|
||||
|
||||
export interface PluginsConfig {
|
||||
[key]: Array<PluginConfig>;
|
||||
}
|
||||
|
||||
export interface PluginConfig {
|
||||
pluginKey: string;
|
||||
type: string;
|
||||
props: object;
|
||||
config: NpmConfig;
|
||||
pluginProps: object;
|
||||
}
|
||||
|
||||
export type HooksConfig = Array<HookConfig>;
|
||||
|
||||
export interface HookConfig {}
|
||||
186
packages/editor/src/framework/editor.ts
Normal file
186
packages/editor/src/framework/editor.ts
Normal file
@ -0,0 +1,186 @@
|
||||
import EventEmitter from 'events';
|
||||
import Debug from 'debug';
|
||||
import store from 'store';
|
||||
|
||||
import {
|
||||
unRegistShortCuts,
|
||||
registShortCuts,
|
||||
transformToPromise,
|
||||
generateI18n,
|
||||
} from './utils';
|
||||
|
||||
// 根据url参数设置debug选项
|
||||
const res = /_?debug=(.*?)(&|$)/.exec(location.search);
|
||||
if (res && res[1]) {
|
||||
window.__isDebug = true;
|
||||
store.storage.write('debug', res[1] === 'true' ? '*' : res[1]);
|
||||
} else {
|
||||
window.__isDebug = false;
|
||||
store.remove('debug');
|
||||
}
|
||||
|
||||
//重要,用于矫正画布执行new Function的window对象上下文
|
||||
window.__newFunc = funContext => {
|
||||
return new Function(funContext);
|
||||
};
|
||||
|
||||
//关闭浏览器前提醒,只有产生过交互才会生效
|
||||
window.onbeforeunload = function(e) {
|
||||
e = e || window.event;
|
||||
// 本地调试不生效
|
||||
if (location.href.indexOf('localhost') > 0) return;
|
||||
var msg = '您确定要离开此页面吗?';
|
||||
e.cancelBubble = true;
|
||||
e.returnValue = msg;
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
let instance = null;
|
||||
const debug = Debug('editor');
|
||||
EventEmitter.defaultMaxListeners = 100;
|
||||
|
||||
|
||||
export default class Editor extends EventEmitter {
|
||||
static getInstance = () => {
|
||||
if (!instance) {
|
||||
instance = new Editor();
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
|
||||
constructor(config, utils, components) {
|
||||
super();
|
||||
instance = this;
|
||||
this.config = config;
|
||||
this.utils = utils;
|
||||
this.components = components;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const { hooks, shortCuts, lifeCycles } = this.config || {};
|
||||
this.locale = store.get('lowcode-editor-locale') || 'zh-CN';
|
||||
// this.messages = this.messagesSet[this.locale];
|
||||
// this.i18n = generateI18n(this.locale, this.messages);
|
||||
this.pluginStatus = this.initPluginStatus();
|
||||
this.initHooks(hooks);
|
||||
|
||||
this.emit('editor.beforeInit');
|
||||
const init = (lifeCycles && lifeCycles.init) || (() => {});
|
||||
// 用户可以通过设置extensions.init自定义初始化流程;
|
||||
return transformToPromise(init(this))
|
||||
.then(() => {
|
||||
// 注册快捷键
|
||||
registShortCuts(shortCuts, this);
|
||||
this.emit('editor.afterInit');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
try {
|
||||
const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config;
|
||||
unRegistShortCuts(shortCuts);
|
||||
this.destroyHooks(hooks);
|
||||
lifeCycles.destroy && lifeCycles.destroy();
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
get(key: string): any {
|
||||
return this[key];
|
||||
}
|
||||
|
||||
set(key: string | object, val: any): void {
|
||||
if (typeof key === 'string') {
|
||||
if (
|
||||
[
|
||||
'init',
|
||||
'destroy',
|
||||
'get',
|
||||
'set',
|
||||
'batchOn',
|
||||
'batchOff',
|
||||
'batchOnce',
|
||||
].includes(key)
|
||||
) {
|
||||
console.warning(
|
||||
'init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute',
|
||||
);
|
||||
return;
|
||||
}
|
||||
this[key] = val;
|
||||
} else if (typeof key === 'object') {
|
||||
Object.keys(key).forEach(item => {
|
||||
this[item] = key[item];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
batchOn(events: Array<string>, lisenter: function): void {
|
||||
if (!Array.isArray(events)) return;
|
||||
events.forEach(event => this.on(event, lisenter));
|
||||
}
|
||||
|
||||
batchOnce(events: Array<string>, lisenter: function): void {
|
||||
if (!Array.isArray(events)) return;
|
||||
events.forEach(event => this.once(event, lisenter));
|
||||
}
|
||||
|
||||
batchOff(events: Array<string>, lisenter: function): void {
|
||||
if (!Array.isArray(events)) return;
|
||||
events.forEach(event => this.off(event, lisenter));
|
||||
}
|
||||
|
||||
//销毁hooks中的消息监听
|
||||
private destroyHooks(hooks = []) {
|
||||
hooks.forEach((item, idx) => {
|
||||
if (typeof this.__hooksFuncs[idx] === 'function') {
|
||||
this.off(item.message, this.__hooksFuncs[idx]);
|
||||
}
|
||||
});
|
||||
delete this.__hooksFuncs;
|
||||
}
|
||||
|
||||
//初始化hooks中的消息监听
|
||||
private initHooks(hooks = []) {
|
||||
this.__hooksFuncs = hooks.map(item => {
|
||||
const func = (...args) => {
|
||||
item.handler(this, ...args);
|
||||
};
|
||||
this[item.type](item.message, func);
|
||||
return func;
|
||||
});
|
||||
}
|
||||
|
||||
private initPluginStatus() {
|
||||
const { plugins = {} } = this.config;
|
||||
const pluginAreas = Object.keys(plugins);
|
||||
const res = {};
|
||||
pluginAreas.forEach(area => {
|
||||
(plugins[area] || []).forEach(plugin => {
|
||||
if (plugin.type === 'Divider') return;
|
||||
const { visible, disabled, dotted } = plugin.props || {};
|
||||
res[plugin.pluginKey] = {
|
||||
visible: typeof visible === 'boolean' ? visible : true,
|
||||
disabled: typeof disabled === 'boolean' ? disabled : false,
|
||||
dotted: typeof dotted === 'boolean' ? dotted : false,
|
||||
};
|
||||
const pluginClass = this.components[plugin.pluginKey];
|
||||
// 判断如果编辑器插件有init静态方法,则在此执行init方法
|
||||
if (pluginClass && pluginClass.init) {
|
||||
pluginClass.init(this);
|
||||
}
|
||||
});
|
||||
});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
3
packages/editor/src/framework/index.ts
Normal file
3
packages/editor/src/framework/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import Editor from './editor';
|
||||
|
||||
export default Editor;
|
||||
54
packages/editor/src/framework/plugin.js
Normal file
54
packages/editor/src/framework/plugin.js
Normal file
@ -0,0 +1,54 @@
|
||||
import React, { PureComponent, creatRef} from 'react';
|
||||
|
||||
import EditorContext from './context';
|
||||
import { isEmpty, generateI18n, goldlog } from './utils';
|
||||
|
||||
|
||||
export default function plugin(Comp) {
|
||||
class LowcodePlugin extends PureComponent {
|
||||
static displayName = 'LowcodeEditorPlugin';
|
||||
static defaultProps = {
|
||||
config: {},
|
||||
};
|
||||
static contextType = EditorContext;
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
if (isEmpty(props.config) || !props.config.pluginKey) {
|
||||
console.warn('lowcode editor plugin has wrong config');
|
||||
return;
|
||||
}
|
||||
this.ref = React.createRef();
|
||||
const { locale, messages, editor } = props;
|
||||
// 注册插件
|
||||
this.editor = editor;
|
||||
this.i18n = generateI18n(locale, messages);
|
||||
this.pluginKey = props.config.pluginKey;
|
||||
editor.plugins = editor.plugins || {};
|
||||
editor.plugins[this.pluginKey] = this;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// 销毁插件
|
||||
if (this.editor && this.editor.plugins) {
|
||||
delete this.editor.plugins[this.pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { config } = this.props;
|
||||
return (
|
||||
<Comp
|
||||
ref={this.ref}
|
||||
i18n={this.i18n}
|
||||
editor={this.editor}
|
||||
config={config}
|
||||
{...config.pluginProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LowcodePlugin.init = Comp.init;
|
||||
|
||||
return LowcodePlugin;
|
||||
}
|
||||
320
packages/editor/src/framework/utils.ts
Normal file
320
packages/editor/src/framework/utils.ts
Normal file
@ -0,0 +1,320 @@
|
||||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import keymaster from 'keymaster';
|
||||
import _isEmpty from 'lodash/isEmpty';
|
||||
|
||||
export const isEmpty = _isEmpty;
|
||||
|
||||
/**
|
||||
* 用于构造国际化字符串处理函数
|
||||
* @param {*} locale 国际化标识,例如 zh-CN、en-US
|
||||
* @param {*} messages 国际化语言包
|
||||
*/
|
||||
export function generateI18n(locale = 'zh-CN', messages = {}) {
|
||||
return (key, values = {}) => {
|
||||
if (!messages || !messages[key]) return '';
|
||||
const formater = new IntlMessageFormat(messages[key], locale);
|
||||
return formater.format(values);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化参数
|
||||
* @param {*} obj 参数
|
||||
*/
|
||||
export function serializeParams(obj: object): string {
|
||||
if (typeof obj !== 'object') return '';
|
||||
|
||||
const res: Array<string> = [];
|
||||
Object.entries(obj).forEach(([key, val]) => {
|
||||
if (val === null || val === undefined || val === '') return;
|
||||
if (typeof val === 'object') {
|
||||
res.push(
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`,
|
||||
);
|
||||
} else {
|
||||
res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);
|
||||
}
|
||||
});
|
||||
return res.join('&');
|
||||
}
|
||||
|
||||
/**
|
||||
* 黄金令箭埋点
|
||||
* @param {String} gmKey 为黄金令箭业务类型
|
||||
* @param {Object} params 参数
|
||||
* @param {String} logKey 属性串
|
||||
*/
|
||||
export function goldlog(gmKey, params = {}, logKey = 'other') {
|
||||
const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage;
|
||||
const goKey = serializeParams({
|
||||
sdkVersion: pkg.version,
|
||||
env: getEnv(),
|
||||
...params,
|
||||
});
|
||||
if (sendIDEMessage) {
|
||||
sendIDEMessage({
|
||||
action: 'goldlog',
|
||||
data: {
|
||||
logKey: `/iceluna.core.${logKey}`,
|
||||
gmKey,
|
||||
goKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
window.goldlog &&
|
||||
window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前编辑器环境
|
||||
*/
|
||||
export function getEnv() {
|
||||
const userAgent = navigator.userAgent;
|
||||
const isVscode = /Electron\//.test(userAgent);
|
||||
if (isVscode) return ENV.VSCODE;
|
||||
const isTheia = window.is_theia === true;
|
||||
if (isTheia) return ENV.WEBIDE;
|
||||
return ENV.WEB;
|
||||
}
|
||||
|
||||
// 注册快捷键
|
||||
export function registShortCuts(config, editor) {
|
||||
const keyboardFilter = (keymaster.filter = event => {
|
||||
let eTarget = event.target || event.srcElement;
|
||||
let tagName = eTarget.tagName;
|
||||
let isInput = !!(
|
||||
tagName == 'INPUT' ||
|
||||
tagName == 'SELECT' ||
|
||||
tagName == 'TEXTAREA'
|
||||
);
|
||||
let isContenteditable = !!eTarget.getAttribute('contenteditable');
|
||||
if (isInput || isContenteditable) {
|
||||
if (event.metaKey === true && [70, 83].includes(event.keyCode))
|
||||
event.preventDefault(); //禁止触发chrome原生的页面保存或查找
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const ideMessage = editor.utils && editor.utils.ideMessage;
|
||||
|
||||
//复制
|
||||
if (!document.copyListener) {
|
||||
document.copyListener = e => {
|
||||
if (!keyboardFilter(e) || editor.isCopying) return;
|
||||
const schema =
|
||||
editor.schemaHelper &&
|
||||
editor.schemaHelper.schemaMap[editor.activeKey];
|
||||
if (!schema || !isSchema(schema)) return;
|
||||
editor.isCopying = true;
|
||||
const schemaStr = serialize(transformSchemaToPure(schema), {
|
||||
unsafe: true,
|
||||
});
|
||||
setClipboardData(schemaStr)
|
||||
.then(() => {
|
||||
ideMessage &&
|
||||
ideMessage(
|
||||
'success',
|
||||
'当前内容已复制到剪贴板,请使用快捷键Command+v进行粘贴',
|
||||
);
|
||||
editor.emit('schema.copy', schemaStr, schema);
|
||||
editor.isCopying = false;
|
||||
})
|
||||
.catch(errMsg => {
|
||||
ideMessage && ideMessage('error', errMsg);
|
||||
editor.isCopying = false;
|
||||
});
|
||||
};
|
||||
document.addEventListener('copy', document.copyListener);
|
||||
if (window.parent.vscode) {
|
||||
keymaster('command+c', document.copyListener);
|
||||
}
|
||||
}
|
||||
|
||||
//粘贴
|
||||
if (!document.pasteListener) {
|
||||
const doPaste = (e, text) => {
|
||||
if (!keyboardFilter(e) || editor.isPasting) return;
|
||||
const schemaHelper = editor.schemaHelper;
|
||||
let targetKey = editor.activeKey;
|
||||
let direction = 'after';
|
||||
const topKey =
|
||||
schemaHelper.schema &&
|
||||
schemaHelper.schema.__ctx &&
|
||||
schemaHelper.schema.__ctx.lunaKey;
|
||||
if (!targetKey || topKey === targetKey) {
|
||||
const schemaHelper = editor.schemaHelper;
|
||||
const topKey =
|
||||
schemaHelper.schema &&
|
||||
schemaHelper.schema.__ctx &&
|
||||
schemaHelper.schema.__ctx.lunaKey;
|
||||
if (!topKey) return;
|
||||
targetKey = topKey;
|
||||
direction = 'in';
|
||||
}
|
||||
editor.isPasting = true;
|
||||
const schema = parseObj(text);
|
||||
if (!isSchema(schema)) {
|
||||
editor.emit('illegalSchema.paste', text);
|
||||
// ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!');
|
||||
console.warn('paste schema illegal');
|
||||
editor.isPasting = false;
|
||||
return;
|
||||
}
|
||||
editor.emit('material.add', {
|
||||
schema,
|
||||
targetKey,
|
||||
direction,
|
||||
});
|
||||
editor.isPasting = false;
|
||||
editor.emit('schema.paste', schema);
|
||||
};
|
||||
document.pasteListener = e => {
|
||||
const clipboardData = e.clipboardData || window.clipboardData;
|
||||
const text = clipboardData && clipboardData.getData('text');
|
||||
doPaste(e, text);
|
||||
};
|
||||
document.addEventListener('paste', document.pasteListener);
|
||||
if (window.parent.vscode) {
|
||||
keymaster('command+v', e => {
|
||||
const sendIDEMessage = window.parent.sendIDEMessage;
|
||||
sendIDEMessage &&
|
||||
sendIDEMessage({
|
||||
action: 'readClipboard',
|
||||
})
|
||||
.then(text => {
|
||||
doPaste(e, text);
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
(config || []).forEach(item => {
|
||||
keymaster(item.keyboard, ev => {
|
||||
ev.preventDefault();
|
||||
item.handler(ev, editor, keymaster);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 取消注册快捷
|
||||
export function unRegistShortCuts(config) {
|
||||
(config || []).forEach(item => {
|
||||
keymaster.unbind(item.keyboard);
|
||||
});
|
||||
if (window.parent.vscode) {
|
||||
keymaster.unbind('command+c');
|
||||
keymaster.unbind('command+v');
|
||||
}
|
||||
if (document.copyListener) {
|
||||
document.removeEventListener('copy', document.copyListener);
|
||||
delete document.copyListener;
|
||||
}
|
||||
if (document.pasteListener) {
|
||||
document.removeEventListener('paste', document.pasteListener);
|
||||
delete document.pasteListener;
|
||||
}
|
||||
}
|
||||
|
||||
// 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve
|
||||
export function transformToPromise(input) {
|
||||
if (input instanceof Promise) return input;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (input || input === undefined) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function transformArrayToMap(arr, key, overwrite = true) {
|
||||
if (isEmpty(arr) || !Array.isArray(arr)) return {};
|
||||
const res = {};
|
||||
arr.forEach(item => {
|
||||
const curKey = item[key];
|
||||
if (item[key] === undefined) return;
|
||||
if (res[curKey] && !overwrite) return;
|
||||
res[curKey] = item;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
export function parseSearch(search) {
|
||||
if (!search || typeof search !== 'string') return {};
|
||||
const str = search.replace(/^\?/, '');
|
||||
let paramStr = str.split('&');
|
||||
let res = {};
|
||||
for (let i = 0; i < paramStr.length; i++) {
|
||||
let regRes = paramStr[i].split('=');
|
||||
if (regRes[0] && regRes[1]) {
|
||||
res[regRes[0]] = decodeURIComponent(regRes[1]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function comboEditorConfig(defaultConfig = {}, customConfig = {}) {
|
||||
const {
|
||||
skeleton,
|
||||
theme,
|
||||
plugins,
|
||||
hooks,
|
||||
shortCuts,
|
||||
lifeCycles,
|
||||
constants,
|
||||
utils,
|
||||
i18n,
|
||||
} = customConfig || {};
|
||||
|
||||
if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') {
|
||||
return skeleton.handler({
|
||||
skeleton,
|
||||
...defaultConfig,
|
||||
});
|
||||
}
|
||||
|
||||
const defaultShortCuts = transformArrayToMap(
|
||||
defaultConfig.shortCuts,
|
||||
'keyboard',
|
||||
);
|
||||
const customShortCuts = transformArrayToMap(shortCuts, 'keyboard');
|
||||
const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP'];
|
||||
const i18nConfig = {};
|
||||
localeList.forEach(key => {
|
||||
i18nConfig[key] = {
|
||||
...(defaultConfig.i18n && defaultConfig.i18n[key]),
|
||||
...(i18n && i18n[key]),
|
||||
};
|
||||
});
|
||||
return {
|
||||
skeleton,
|
||||
theme: {
|
||||
...defaultConfig.theme,
|
||||
...theme,
|
||||
},
|
||||
plugins: {
|
||||
...defaultConfig.plugins,
|
||||
...plugins,
|
||||
},
|
||||
hooks: [...(defaultConfig.hooks || []), ...(hooks || [])],
|
||||
shortCuts: Object.values({
|
||||
...defaultShortCuts,
|
||||
...customShortCuts,
|
||||
}),
|
||||
lifeCycles: {
|
||||
...defaultConfig.lifeCycles,
|
||||
...lifeCycles,
|
||||
},
|
||||
constants: {
|
||||
...defaultConfig.constants,
|
||||
...constants,
|
||||
},
|
||||
utils: [...(defaultConfig.utils || []), ...(utils || [])],
|
||||
i18n: i18nConfig,
|
||||
};
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
body {
|
||||
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light,
|
||||
Microsoft YaHei;
|
||||
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma,
|
||||
Arial, PingFang SC-Light, Microsoft YaHei;
|
||||
font-size: 12px;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { HashRouter as Router, Route } from 'react-router-dom';
|
||||
import Skeleton from '@ali/lowcode-engine-skeleton';
|
||||
|
||||
// import Skeleton from '@ali/lowcode-engine-skeleton';
|
||||
import Skeleton from './skeleton';
|
||||
import config from './config/skeleton';
|
||||
import components from './config/components';
|
||||
import componentsMap from './config/componentsMap';
|
||||
import utils from './config/utils';
|
||||
import constants from './config/constants';
|
||||
import messages from './config/locale';
|
||||
@ -22,21 +21,12 @@ if (!ICE_CONTAINER) {
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Router>
|
||||
<Route
|
||||
path="/*"
|
||||
component={props => {
|
||||
return (
|
||||
<Skeleton
|
||||
{...props}
|
||||
{...(config.skeleton && config.skeleton.props)}
|
||||
config={config}
|
||||
utils={utils}
|
||||
messages={messages}
|
||||
constants={constants}
|
||||
pluginComponents={components}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Router>, ICE_CONTAINER);
|
||||
<Skeleton
|
||||
{...(config.skeleton && config.skeleton.props)}
|
||||
config={config}
|
||||
utils={utils}
|
||||
constants={constants}
|
||||
components={components}
|
||||
/>,
|
||||
ICE_CONTAINER,
|
||||
);
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
.lowcode-left-plugin {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
height: 36px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: #777;
|
||||
&.collapse {
|
||||
height: 40px;
|
||||
color: #8c8c8c;
|
||||
border-bottom: 1px solid #bfbfbf;
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: #fff !important;
|
||||
background-color: $color-brand1-9 !important;
|
||||
&.disabled {
|
||||
color: #fff;
|
||||
background-color: $color-fill1-7;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&:hover {
|
||||
background-color: $color-brand1-1;
|
||||
color: $color-brand1-6;
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
top: 5px;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
color: #fff;
|
||||
z-index: 100;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 15px;
|
||||
border: 5px solid transparent;
|
||||
border-right-color: rgba(0, 0, 0, 0.75);
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
205
packages/editor/src/skeleton/components/LeftPlugin/index.tsx
Normal file
205
packages/editor/src/skeleton/components/LeftPlugin/index.tsx
Normal file
@ -0,0 +1,205 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Balloon, Dialog, Icon, Badge } from '@alife/next';
|
||||
|
||||
import './index.scss';
|
||||
export default class LeftPlugin extends PureComponent {
|
||||
static displayName = 'lowcodeLeftPlugin';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
dialogVisible: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// const { config } = this.props;
|
||||
// const addonKey = config && config.addonKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.on(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.on(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// const { config } = this.props;
|
||||
// const appHelper = this.appHelper;
|
||||
// const addonKey = config && config.addonKey;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.off(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.off(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
// const addonKey = this.props.config && this.props.config.addonKey;
|
||||
// const currentAddon =
|
||||
// this.appHelper.addons && this.appHelper.addons[addonKey];
|
||||
// if (currentAddon) {
|
||||
// this.utils.transformToPromise(currentAddon.close()).then(() => {
|
||||
// this.setState({
|
||||
// dialogVisible: false,
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
// todo 对话框类型的插件初始时拿不到插件实例
|
||||
this.setState({
|
||||
dialogVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleShow = () => {
|
||||
// const { disabled, config, onClick } = this.props;
|
||||
// const addonKey = config && config.addonKey;
|
||||
// if (disabled || !addonKey) return;
|
||||
// //考虑到弹窗情况,延时发送消息
|
||||
// setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0);
|
||||
// this.handleOpen();
|
||||
// onClick && onClick();
|
||||
};
|
||||
|
||||
renderIcon = clickCallback => {
|
||||
const {
|
||||
active,
|
||||
disabled,
|
||||
dotted,
|
||||
locked,
|
||||
onClick,
|
||||
config,
|
||||
editor,
|
||||
} = this.props;
|
||||
const { pluginKey, props } = config || {};
|
||||
const { icon, title } = props || {};
|
||||
return (
|
||||
<div
|
||||
className={classNames('lowcode-left-plugin', pluginKey, {
|
||||
active,
|
||||
disabled,
|
||||
locked,
|
||||
})}
|
||||
data-tooltip={title}
|
||||
onClick={() => {
|
||||
if (disabled) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
clickCallback && clickCallback();
|
||||
onClick && onClick();
|
||||
}}
|
||||
>
|
||||
{dotted ? (
|
||||
<Badge dot>
|
||||
<Icon type={icon} size="small" />
|
||||
</Badge>
|
||||
) : (
|
||||
<Icon type={icon} size="small" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
dotted,
|
||||
locked,
|
||||
active,
|
||||
disabled,
|
||||
config,
|
||||
editor,
|
||||
pluginClass: Comp,
|
||||
} = this.props;
|
||||
const { pluginKey, props, type, pluginProps } = config || {};
|
||||
const { onClick, title } = props || {};
|
||||
const { dialogVisible } = this.state;
|
||||
if (!pluginKey || !type || !props) return null;
|
||||
|
||||
const node =
|
||||
(Comp && (
|
||||
<Comp
|
||||
editor={editor}
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
}}
|
||||
{...pluginProps}
|
||||
/>
|
||||
)) ||
|
||||
null;
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return (
|
||||
<a {...(props.linkProps || {})}>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
</a>
|
||||
);
|
||||
case 'Icon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
});
|
||||
case 'DialogIcon':
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
})}
|
||||
<Dialog
|
||||
onOk={() => {
|
||||
editor.emit(`${pluginKey}.dialog.onOk`);
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={this.handleClose}
|
||||
onClose={this.handleClose}
|
||||
title={title}
|
||||
{...(props.dialogProps || {})}
|
||||
visible={dialogVisible}
|
||||
>
|
||||
{node}
|
||||
</Dialog>
|
||||
</Fragment>
|
||||
);
|
||||
case 'BalloonIcon':
|
||||
return (
|
||||
<Balloon
|
||||
trigger={this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
align="r"
|
||||
triggerType={['click', 'hover']}
|
||||
{...(props.balloonProps || {})}
|
||||
>
|
||||
{node}
|
||||
</Balloon>
|
||||
);
|
||||
case 'PanelIcon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
});
|
||||
case 'Custom':
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
packages/editor/src/skeleton/components/Panel/index.scss
Normal file
52
packages/editor/src/skeleton/components/Panel/index.scss
Normal file
@ -0,0 +1,52 @@
|
||||
.lowcode-panel {
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #ffffff;
|
||||
transition: width 0.3s ease;
|
||||
transform: translate3d(0, 0, 0);
|
||||
height: 100%;
|
||||
&.visible {
|
||||
border-right: 1px solid #bfbfbf;
|
||||
}
|
||||
.drag-area {
|
||||
display: none;
|
||||
}
|
||||
&.floatable {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
&.draggable {
|
||||
.drag-area {
|
||||
display: block;
|
||||
width: 10px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
cursor: col-resize;
|
||||
z-index: 9999;
|
||||
}
|
||||
&.left {
|
||||
.drag-area {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
&.right {
|
||||
.drag-area {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.left {
|
||||
&.floatable {
|
||||
left: 44px;
|
||||
}
|
||||
}
|
||||
&.right {
|
||||
&.floatable {
|
||||
right: 44px;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
packages/editor/src/skeleton/components/Panel/index.tsx
Normal file
23
packages/editor/src/skeleton/components/Panel/index.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import './index.scss';
|
||||
export default class Panel extends PureComponent {
|
||||
static displayName = 'Panel';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className="lowcode-panel"
|
||||
style={{
|
||||
width: 240,
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
32
packages/editor/src/skeleton/components/TopIcon/index.scss
Normal file
32
packages/editor/src/skeleton/components/TopIcon/index.scss
Normal file
@ -0,0 +1,32 @@
|
||||
.next-btn.next-large.lowcode-top-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
margin: 2px -2px;
|
||||
text-align: center;
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
color: #777;
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
i.next-icon {
|
||||
&:before {
|
||||
font-size: 17px;
|
||||
}
|
||||
margin-right: 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
margin: 0px -5px 0;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
68
packages/editor/src/skeleton/components/TopIcon/index.tsx
Normal file
68
packages/editor/src/skeleton/components/TopIcon/index.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Icon, Button } from '@alifd/next';
|
||||
import './index.scss';
|
||||
export default class TopIcon extends PureComponent {
|
||||
static displayName = 'TopIcon';
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
icon: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
locked: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
showTitle: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
className: '',
|
||||
disabled: false,
|
||||
icon: '',
|
||||
id: '',
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
showTitle: false,
|
||||
style: {},
|
||||
title: '',
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
active,
|
||||
disabled,
|
||||
icon,
|
||||
locked,
|
||||
title,
|
||||
className,
|
||||
id,
|
||||
style,
|
||||
showTitle,
|
||||
onClick,
|
||||
} = this.props;
|
||||
return (
|
||||
<Button
|
||||
type="normal"
|
||||
size="large"
|
||||
text={true}
|
||||
className={classNames('lowcode-top-btn', className, {
|
||||
active,
|
||||
disabled,
|
||||
locked,
|
||||
})}
|
||||
id={id}
|
||||
style={style}
|
||||
onClick={disabled ? null : onClick}
|
||||
>
|
||||
<div>
|
||||
<Icon size="large" type={icon} />
|
||||
{showTitle && <span>{title}</span>}
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
.lowcode-top-addon {
|
||||
}
|
||||
192
packages/editor/src/skeleton/components/TopPlugin/index.tsx
Normal file
192
packages/editor/src/skeleton/components/TopPlugin/index.tsx
Normal file
@ -0,0 +1,192 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import TopIcon from '../TopIcon';
|
||||
import { Balloon, Badge, Dialog } from '@alifd/next';
|
||||
|
||||
import './index.scss';
|
||||
export default class TopPlugin extends PureComponent {
|
||||
static displayName = 'lowcodeTopPlugin';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
dialogVisible: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { config } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && pluginKey) {
|
||||
// appHelper.on(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.on(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// const { config } = this.props;
|
||||
// const pluginKey = config && config.pluginKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && pluginKey) {
|
||||
// appHelper.off(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.off(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
}
|
||||
|
||||
handleShow = () => {
|
||||
// const { disabled, config, onClick, editor } = this.props;
|
||||
// const pluginKey = config && config.pluginKey;
|
||||
// if (disabled || !pluginKey) return;
|
||||
// //考虑到弹窗情况,延时发送消息
|
||||
// setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0);
|
||||
// this.handleOpen();
|
||||
// onClick && onClick();
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
// const pluginKey = this.props.config && this.props.config.pluginKey;
|
||||
// const currentAddon =
|
||||
// this.appHelper.addons && this.appHelper.addons[pluginKey];
|
||||
// if (currentAddon) {
|
||||
// this.utils.transformToPromise(currentAddon.close()).then(() => {
|
||||
// this.setState({
|
||||
// dialogVisible: false,
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
// todo dialog类型的插件初始时拿不动插件实例
|
||||
this.setState({
|
||||
dialogVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
renderIcon = clickCallback => {
|
||||
const {
|
||||
active,
|
||||
disabled,
|
||||
dotted,
|
||||
locked,
|
||||
config,
|
||||
onClick,
|
||||
editor,
|
||||
} = this.props;
|
||||
const { pluginKey, props } = config || {};
|
||||
const { icon, title } = props || {};
|
||||
const node = (
|
||||
<TopIcon
|
||||
className={`lowcode-top-addon ${pluginKey}`}
|
||||
active={active}
|
||||
disabled={disabled}
|
||||
locked={locked}
|
||||
icon={icon}
|
||||
title={title}
|
||||
onClick={() => {
|
||||
if (disabled) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0);
|
||||
clickCallback && clickCallback();
|
||||
onClick && onClick();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
active,
|
||||
dotted,
|
||||
locked,
|
||||
disabled,
|
||||
config,
|
||||
editor,
|
||||
pluginClass: Comp,
|
||||
} = this.props;
|
||||
const { pluginKey, pluginProps, props, type } = config || {};
|
||||
const { onClick, title } = props || {};
|
||||
const { dialogVisible } = this.state;
|
||||
if (!pluginKey || !type) return null;
|
||||
const node =
|
||||
(Comp && (
|
||||
<Comp
|
||||
editor={editor}
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
}}
|
||||
{...pluginProps}
|
||||
/>
|
||||
)) ||
|
||||
null;
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return (
|
||||
<a {...props.linkProps}>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
</a>
|
||||
);
|
||||
case 'Icon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
});
|
||||
case 'DialogIcon':
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
})}
|
||||
<Dialog
|
||||
onOk={() => {
|
||||
editor.emit(`${pluginKey}.dialog.onOk`);
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={this.handleClose}
|
||||
onClose={this.handleClose}
|
||||
title={title}
|
||||
{...props.dialogProps}
|
||||
visible={dialogVisible}
|
||||
>
|
||||
{node}
|
||||
</Dialog>
|
||||
</Fragment>
|
||||
);
|
||||
case 'BalloonIcon':
|
||||
return (
|
||||
<Balloon
|
||||
trigger={this.renderIcon(() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
triggerType={['click', 'hover']}
|
||||
{...props.balloonProps}
|
||||
>
|
||||
{node}
|
||||
</Balloon>
|
||||
);
|
||||
case 'Custom':
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
packages/editor/src/skeleton/config/skeleton.ts
Normal file
1
packages/editor/src/skeleton/config/skeleton.ts
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
1
packages/editor/src/skeleton/config/utils.ts
Normal file
1
packages/editor/src/skeleton/config/utils.ts
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
33
packages/editor/src/skeleton/global.scss
Normal file
33
packages/editor/src/skeleton/global.scss
Normal file
@ -0,0 +1,33 @@
|
||||
body {
|
||||
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma,
|
||||
Arial, PingFang SC-Light, Microsoft YaHei;
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.next-loading {
|
||||
.next-loading-wrap {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.lowcode-editor {
|
||||
.lowcode-main-content {
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
.lowcode-center-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
129
packages/editor/src/skeleton/index.tsx
Normal file
129
packages/editor/src/skeleton/index.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { HashRouter as Router, Route } from 'react-router-dom';
|
||||
import Editor from '../framework/editor';
|
||||
import { comboEditorConfig, parseSearch } from '../framework/utils';
|
||||
import { Loading, ConfigProvider } from '@alifd/next';
|
||||
import defaultConfig from './config/skeleton';
|
||||
import skeletonUtils from './config/utils';
|
||||
|
||||
import TopArea from './layouts/TopArea';
|
||||
import LeftArea from './layouts/LeftArea';
|
||||
import CenterArea from './layouts/CenterArea';
|
||||
import RightArea from './layouts/RightArea';
|
||||
|
||||
import './global.scss';
|
||||
|
||||
let renderIdx = 0;
|
||||
|
||||
export default class Skeleton extends PureComponent {
|
||||
static displayName = 'LowcodeEditorSkeleton';
|
||||
|
||||
static getDerivedStateFromError() {
|
||||
return {
|
||||
__hasError: true,
|
||||
};
|
||||
}
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
initReady: false,
|
||||
skeletonKey: `skeleton${renderIdx}`,
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.editor && this.editor.destroy();
|
||||
this.editor = null;
|
||||
}
|
||||
|
||||
componentDidCatch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
init = (isReset = false) => {
|
||||
if (this.editor) {
|
||||
this.editor.destroy();
|
||||
this.editor = null;
|
||||
}
|
||||
const { utils, config, components } = this.props;
|
||||
const editor = (this.editor = new Editor(
|
||||
comboEditorConfig(defaultConfig, config),
|
||||
{ ...skeletonUtils, ...utils },
|
||||
components,
|
||||
));
|
||||
window.__ctx = {
|
||||
editor,
|
||||
appHelper: editor
|
||||
};
|
||||
editor.once('editor.reset', () => {
|
||||
this.setState({
|
||||
initReady: false,
|
||||
});
|
||||
editor.emit('editor.beforeReset');
|
||||
this.init(true);
|
||||
});
|
||||
|
||||
this.editor.init().then(() => {
|
||||
this.setState(
|
||||
{
|
||||
initReady: true,
|
||||
//刷新IDE时生成新的skeletonKey保证插件生命周期重新执行
|
||||
skeletonKey: isReset
|
||||
? `skeleton${++renderIdx}`
|
||||
: this.state.skeletonKey,
|
||||
},
|
||||
() => {
|
||||
editor.emit('editor.ready');
|
||||
isReset && editor.emit('ide.afterReset');
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { initReady, skeletonKey, __hasError } = this.state;
|
||||
if (__hasError) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Route
|
||||
path="/*"
|
||||
component={props => {
|
||||
const { location, history, match } = props;
|
||||
location.query = parseSearch(location.search);
|
||||
this.editor.set('location', location);
|
||||
this.editor.set('history', history);
|
||||
this.editor.set('match', match);
|
||||
return (
|
||||
<ConfigProvider>
|
||||
<Loading
|
||||
tip="Loading"
|
||||
size="large"
|
||||
visible={!initReady}
|
||||
shape="fusion-reactor"
|
||||
fullScreen
|
||||
>
|
||||
<div className="lowcode-editor" key={skeletonKey}>
|
||||
<TopArea editor={this.editor} />
|
||||
<div className="lowcode-main-content">
|
||||
<LeftArea.Nav editor={this.editor} />
|
||||
<LeftArea.Panel editor={this.editor} />
|
||||
<CenterArea editor={this.editor} />
|
||||
<RightArea editor={this.editor} />
|
||||
</div>
|
||||
</div>
|
||||
</Loading>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
.lowcode-center-area {
|
||||
padding: 12px;
|
||||
}
|
||||
33
packages/editor/src/skeleton/layouts/CenterArea/index.tsx
Normal file
33
packages/editor/src/skeleton/layouts/CenterArea/index.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export default class CenterArea extends PureComponent {
|
||||
static displayName = 'lowcodeCenterArea';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.config = this.editor.config && this.editor.config.plugins && this.editor.config.plugins.centerArea || [];
|
||||
}
|
||||
|
||||
render() {
|
||||
const list = this.config.filter(item => {
|
||||
return true;
|
||||
});
|
||||
return (
|
||||
<div className="lowcode-center-area">
|
||||
{list.map(item => {
|
||||
const Comp = this.editor.components[item.pluginKey];
|
||||
return (
|
||||
<Comp
|
||||
editor={this.editor}
|
||||
config={item}
|
||||
{...item.pluginProps}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
21
packages/editor/src/skeleton/layouts/LeftArea/index.scss
Normal file
21
packages/editor/src/skeleton/layouts/LeftArea/index.scss
Normal file
@ -0,0 +1,21 @@
|
||||
.lowcode-left-area-nav {
|
||||
width: 48px;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-right: 1px solid #e8ebee;
|
||||
position: relative;
|
||||
.top-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
max-height: 100%;
|
||||
}
|
||||
.bottom-area {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
max-height: calc(100% - 20px);
|
||||
}
|
||||
}
|
||||
7
packages/editor/src/skeleton/layouts/LeftArea/index.tsx
Normal file
7
packages/editor/src/skeleton/layouts/LeftArea/index.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import Nav from './nav';
|
||||
import Panel from './panel';
|
||||
|
||||
export default {
|
||||
Nav,
|
||||
Panel,
|
||||
};
|
||||
51
packages/editor/src/skeleton/layouts/LeftArea/nav.tsx
Normal file
51
packages/editor/src/skeleton/layouts/LeftArea/nav.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import LeftPlugin from '../../components/LeftPlugin';
|
||||
import './index.scss';
|
||||
|
||||
export default class LeftAreaPanel extends PureComponent {
|
||||
static displayName = 'lowcodeLeftAreaNav';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.config =
|
||||
this.editor.config.plugins && this.editor.config.plugins.leftArea;
|
||||
}
|
||||
|
||||
handlePluginClick = item => {};
|
||||
|
||||
renderPluginList = (list = []) => {
|
||||
return list.map((item, idx) => {
|
||||
return (
|
||||
<LeftPlugin
|
||||
key={item.pluginKey}
|
||||
config={item}
|
||||
editor={this.editor}
|
||||
pluginClass={this.editor.components[item.pluginKey]}
|
||||
onClick={() => this.handlePluginClick(item)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const topList = [];
|
||||
const bottomList = [];
|
||||
this.config.forEach(item => {
|
||||
const align =
|
||||
item.props && item.props.align === 'bottom' ? 'bottom' : 'top';
|
||||
if (align === 'bottom') {
|
||||
bottomList.push(item);
|
||||
} else {
|
||||
topList.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="lowcode-left-area-nav">
|
||||
<div className="bottom-area">{this.renderPluginList(bottomList)}</div>
|
||||
<div className="top-area">{this.renderPluginList(topList)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
39
packages/editor/src/skeleton/layouts/LeftArea/panel.tsx
Normal file
39
packages/editor/src/skeleton/layouts/LeftArea/panel.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import Panel from '../../components/Panel';
|
||||
import './index.scss';
|
||||
|
||||
export default class LeftAreaPanel extends PureComponent {
|
||||
static displayName = 'lowcodeLeftAreaPanel';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.config =
|
||||
this.editor.config.plugins && this.editor.config.plugins.leftArea;
|
||||
|
||||
this.state = {
|
||||
activeKey: 'leftPanelIcon',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const list = this.config.filter(item => {
|
||||
return item.type === 'PanelIcon';
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
{list.map((item, idx) => {
|
||||
const Comp = this.editor.components[item.pluginKey];
|
||||
return (
|
||||
<Panel
|
||||
key={item.pluginKey}
|
||||
visible={item.pluginKey === this.state.activeKey}
|
||||
>
|
||||
<Comp editor={this.editor} config={item} />
|
||||
</Panel>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
157
packages/editor/src/skeleton/layouts/RightArea/index.scss
Normal file
157
packages/editor/src/skeleton/layouts/RightArea/index.scss
Normal file
@ -0,0 +1,157 @@
|
||||
.lowcode-right-area {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
border-left: 1px solid #e8ebee;
|
||||
.right-plugin-title {
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9 !important;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
|
||||
//tab定义
|
||||
.next-tabs-wrapped.right-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: -1px;
|
||||
.next-tabs-bar {
|
||||
z-index: 1;
|
||||
}
|
||||
.next-tabs-nav {
|
||||
display: block;
|
||||
.next-tabs-tab {
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
border-right: none !important;
|
||||
margin-right: 0 !important;
|
||||
width: 25%;
|
||||
&.active {
|
||||
background: none;
|
||||
border-bottom-color: #f7f7f7 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.next-tabs-content {
|
||||
flex: 1;
|
||||
.next-tabs-tabpane.active {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
//组件
|
||||
.select-comp {
|
||||
padding: 10px 16px;
|
||||
line-height: 16px;
|
||||
color: #989a9c;
|
||||
& > span {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
& > .btn-wrap,
|
||||
& > .next-btn {
|
||||
width: auto;
|
||||
margin: 0 5px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.unselected {
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
//右侧属性面板样式调整;
|
||||
.offset-56 {
|
||||
padding-left: 56px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.fixedSpan.next-form-item {
|
||||
& > .next-form-item-label {
|
||||
width: 56px;
|
||||
flex: none;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
}
|
||||
}
|
||||
.fixedSpan.next-form-item,
|
||||
.offset-56 .next-form-item {
|
||||
display: flex;
|
||||
& > .next-form-item-control {
|
||||
width: auto;
|
||||
flex: 1;
|
||||
max-width: none;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topSpan.next-form-item {
|
||||
margin-bottom: 4px;
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
packages/editor/src/skeleton/layouts/RightArea/index.tsx
Normal file
53
packages/editor/src/skeleton/layouts/RightArea/index.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Tab } from '@alifd/next';
|
||||
import './index.scss';
|
||||
|
||||
export default class RightArea extends PureComponent {
|
||||
static displayName = 'lowcodeRightArea';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.state = {
|
||||
activeKey: 'rightPanel1',
|
||||
};
|
||||
}
|
||||
|
||||
handleTabChange = key => {
|
||||
this.setState({
|
||||
activeKey: key,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const list =
|
||||
(this.editor &&
|
||||
this.editor.config &&
|
||||
this.editor.config.plugins &&
|
||||
this.editor.config.plugins.rightArea) ||
|
||||
[];
|
||||
return (
|
||||
<div className="lowcode-right-area">
|
||||
<Tab
|
||||
shape="wrapped"
|
||||
className="right-tabs"
|
||||
style={{
|
||||
height: '100%',
|
||||
}}
|
||||
activeKey={this.state.activeKey}
|
||||
lazyLoad={false}
|
||||
onChange={this.handleTabChange}
|
||||
>
|
||||
{list.map((item, idx) => {
|
||||
const Comp = this.editor.components[item.pluginKey];
|
||||
return (
|
||||
<Tab.Item key={item.pluginKey} title={item.props.title}>
|
||||
<Comp editor={this.editor} config={item.config} {...item.pluginProps} />
|
||||
</Tab.Item>
|
||||
);
|
||||
})}
|
||||
</Tab>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
27
packages/editor/src/skeleton/layouts/TopArea/index.scss
Normal file
27
packages/editor/src/skeleton/layouts/TopArea/index.scss
Normal file
@ -0,0 +1,27 @@
|
||||
.lowcode-top-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #e8ebee;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
.divider {
|
||||
max-width: 0;
|
||||
margin: 8px 12px;
|
||||
height: 30px;
|
||||
border-right: 1px solid rgba(191, 191, 191, 0.3);
|
||||
}
|
||||
.next-col {
|
||||
text-align: center;
|
||||
}
|
||||
.right-area {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
80
packages/editor/src/skeleton/layouts/TopArea/index.tsx
Normal file
80
packages/editor/src/skeleton/layouts/TopArea/index.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Grid } from '@alifd/next';
|
||||
import TopPlugin from '../../components/TopPlugin';
|
||||
import './index.scss';
|
||||
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
export default class TopArea extends PureComponent {
|
||||
static displayName = 'lowcodeTopArea';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.config =
|
||||
this.editor.config.plugins && this.editor.config.plugins.topArea;
|
||||
}
|
||||
|
||||
componentDidMount() {}
|
||||
componentWillUnmount() {}
|
||||
|
||||
handlePluginStatusChange = () => {};
|
||||
|
||||
renderPluginList = (list = []) => {
|
||||
return list.map((item, idx) => {
|
||||
const isDivider = item.type === 'Divider';
|
||||
return (
|
||||
<Col
|
||||
className={isDivider ? 'divider' : ''}
|
||||
key={isDivider ? idx : item.pluginKey}
|
||||
style={{
|
||||
width: (item.props && item.props.width) || 40,
|
||||
flex: 'none',
|
||||
}}
|
||||
>
|
||||
{!isDivider && (
|
||||
<TopPlugin
|
||||
config={item}
|
||||
pluginClass={this.editor.components[item.pluginKey]}
|
||||
editor={this.editor}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.config) return null;
|
||||
const leftList = [];
|
||||
const rightList = [];
|
||||
this.config.forEach(item => {
|
||||
const align =
|
||||
item.props && item.props.align === 'right' ? 'right' : 'left';
|
||||
// 分隔符不允许相邻
|
||||
if (item.type === 'Divider') {
|
||||
const currList = align === 'right' ? rightList : leftList;
|
||||
if (
|
||||
currList.length === 0 ||
|
||||
currList[currList.length - 1].type === 'Divider'
|
||||
)
|
||||
return;
|
||||
}
|
||||
if (align === 'right') {
|
||||
rightList.push(item);
|
||||
} else {
|
||||
leftList.push(item);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div className="lowcode-top-area">
|
||||
<div className="left-area">
|
||||
<Row>{this.renderPluginList(leftList)}</Row>
|
||||
</div>
|
||||
<div className="right-area">
|
||||
<Row justify="end">{this.renderPluginList(rightList)}</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
10
packages/editor/src/skeleton/locale/en-US.js
Normal file
10
packages/editor/src/skeleton/locale/en-US.js
Normal file
@ -0,0 +1,10 @@
|
||||
export default {
|
||||
loading: 'loading...',
|
||||
rejectRedirect: 'Redirect is not allowed',
|
||||
expand: 'Unfold',
|
||||
fold: 'Fold',
|
||||
pageNotExist: 'The current Page not exist',
|
||||
enterFromAppCenter: 'Please enter from the app center',
|
||||
noPermission: 'Sorry, you do not have the develop permission',
|
||||
getPermission: 'Please connect the app owners {owners} to get the permission',
|
||||
};
|
||||
1
packages/editor/src/skeleton/locale/ja-JP.js
Normal file
1
packages/editor/src/skeleton/locale/ja-JP.js
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
10
packages/editor/src/skeleton/locale/zh-CN.js
Normal file
10
packages/editor/src/skeleton/locale/zh-CN.js
Normal file
@ -0,0 +1,10 @@
|
||||
export default {
|
||||
loading: '加载中...',
|
||||
rejectRedirect: '开发中,已阻止发生跳转',
|
||||
expand: '展开',
|
||||
fold: '收起',
|
||||
pageNotExist: '当前访问地址不存在',
|
||||
enterFromAppCenter: '请从应用中心入口重新进入',
|
||||
noPermission: '抱歉,您暂无开发权限',
|
||||
getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限',
|
||||
};
|
||||
1
packages/editor/src/skeleton/locale/zh-TW.js
Normal file
1
packages/editor/src/skeleton/locale/zh-TW.js
Normal file
@ -0,0 +1 @@
|
||||
export default {};
|
||||
Loading…
x
Reference in New Issue
Block a user