diff --git a/packages/editor-skeleton/src/layouts/top-area.tsx b/packages/editor-skeleton/src/layouts/top-area.tsx
index 2b8535117..b9dee5c38 100644
--- a/packages/editor-skeleton/src/layouts/top-area.tsx
+++ b/packages/editor-skeleton/src/layouts/top-area.tsx
@@ -4,23 +4,23 @@ import { observer } from '@ali/lowcode-editor-core';
import Area from '../area';
@observer
-export default class TopArea extends Component<{ area: Area }> {
+export default class TopArea extends Component<{ area: Area, itemClassName?: string }> {
render() {
- const { area } = this.props;
+ const { area, itemClassName } = this.props;
return (
-
-
+
);
}
}
@observer
-class Contents extends Component<{ area: Area }> {
+class Contents extends Component<{ area: Area, itemClassName?: string }> {
render() {
- const { area } = this.props;
+ const { area, itemClassName } = this.props;
const left: any[] = [];
const center: any[] = [];
const right: any[] = [];
@@ -29,12 +29,17 @@ class Contents extends Component<{ area: Area }> {
const index2 = b.config?.index || 0;
return index1 === index2 ? 0 : (index1 > index2 ? 1 : -1);
}).forEach(item => {
+ const content = (
+
+ {item.content}
+
+ );
if (item.align === 'center') {
- center.push(item.content);
+ center.push(content);
} else if (item.align === 'left') {
- left.push(item.content);
+ left.push(content);
} else {
- right.push(item.content);
+ right.push(content);
}
});
return (
diff --git a/packages/editor-skeleton/src/layouts/workbench.less b/packages/editor-skeleton/src/layouts/workbench.less
index 4efca072d..70a07c0ca 100644
--- a/packages/editor-skeleton/src/layouts/workbench.less
+++ b/packages/editor-skeleton/src/layouts/workbench.less
@@ -54,14 +54,11 @@ body {
display: none;
}
.lc-panel-title {
- height: 38px;
- font-size: 14px;
- background-color: var(--pane-title-bg-color,rgba(31,56,88,.04));
+ // background-color: var(--pane-title-bg-color,rgba(31,56,88,.04));
display: flex;
align-items: center;
- justify-content: center;
+ justify-content: flex-start;
padding: 0 15px;
- border-bottom: 1px solid var(--color-line-normal,rgba(31,56,88,.1));
.lc-help-tip {
margin-left: 4px;
@@ -69,10 +66,18 @@ body {
cursor: pointer;
}
}
+ > .lc-panel-title {
+ height: 48px;
+ font-size: 16px;
+ padding: 0 15px;
+ // border-bottom: 1px solid var(--color-line-normal,rgba(31,56,88,.1));
+ color: #0F1726;
+ font-weight: bold;
+ }
.lc-panel-body {
position: absolute;
- top: 38px;
+ top: 48px;
bottom: 0;
left: 0;
right: 0;
@@ -111,6 +116,9 @@ body {
}
*/
}
+ .lc-outline-pane{
+ border-top: 1px solid var(--color-line-normal,rgba(31,56,88,.1));
+ }
}
.lc-panel {
height: 100%;
@@ -153,23 +161,88 @@ body {
}
}
*/
-}
+ /*覆盖旧面板*/
+ /*组件面板*/
+ // .ve-component-list {
+ // .ve-component-list-body{
+ // .ve-component-list-sidebar{
+ // .ve-component-list-navigator{
+ // .navigator-group{
+ // &:last-child{
+ // &::after{
+ // display: none;
+ // }
+ // }
+ // &::after{
+ // content: '';
+ // display: block;
+ // height: 1px;
+ // background-color: #EDEFF3;
+ // line-height: 0;
+ // margin: 4px 12px 0;
+ // }
+ // .navigator-group-head{
+ // .navigator-group-title{
+ // border-bottom: none;
+ // }
+ // }
+ // .navigator-group-item{
+ // border-left: 2px solid transparent;
+ // &.active{
+ // border-left-color: #0079f2;
+ // border-right: none;
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ /*数据源*/
+ // .engine-datapool{
+ // .engine-datapool-view-group{
+ // padding-top: 48px;
+ // .engine-datapool-view-group-title{
+ // height: 48px;
+ // line-height: 48px;
+ // font-size: 16px;
+ // background-color: transparent;
+ // padding: 0 16px;
+ // border-bottom: 1px solid #EDEFF3;
+ // }
+ // }
+ // }
+ /*动作面板*/
+ // .ve-action-pane{
+ // border-top: none;
+ // .rc-tabs{
+ // .rc-tabs-bar{
+ // background-color: transparent;
+ // .rc-tabs-tab{
+ // line-height: 1;
+ // &.rc-tabs-tab-active{
-.my-dock {
- padding: 0px 10px;
- cursor: pointer;
- align-self: stretch;
- display: flex;
- align-items: center;
- .my-title-label {
- user-select: none;
- }
- &.actived, &:hover {
- background-color: var(--pane-title-bg-color);
- .my-title {
- color: var(--color-actived);
- }
- }
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ /*设置面板*/
+ // .ve-field .ve-field-head,
+ // .ve-field.ve-accordion2-field > .ve-field-head .ve-field-title-content{
+ // padding: 0;
+ // }
+ // .ve-field.ve-accordion2-field > .ve-field-split-line{
+ // display: none;
+ // }
+ // .vs-style .vs-style-source{
+ // margin: 0 0 16px;
+ // }
+ // .vs-code-button,
+ // .vs-json-button{
+ // margin: 0;
+ // }
}
@@ -184,13 +257,13 @@ body {
width: 100%;
display: flex;
margin-bottom: 2px;
- padding: 8px;
- .lc-top-area-left{}
+ padding: 8px 12px 8px 16px;
+
.lc-top-area-center{
flex: 1;
display: flex;
- justify-content: flex-end;
- margin-right: 8px;
+ justify-content: center;
+ margin: 0 8px;
}
.lc-top-area-right{
display: flex;
@@ -222,8 +295,9 @@ body {
}
.lc-pane-close{
position: absolute;
- right: 10px;
- top: 6px;
+ right: 16px;
+ top: 16px;
+ height: auto;
z-index: 2;
.next-icon{
line-height: 1;
@@ -231,17 +305,32 @@ body {
}
.lc-tabs-title {
width: 100%;
- height: 36px;
+ height: 32px;
position: relative;
display: center;
display: flex;
justify-content: center;
align-items: center;
- background: rgba(31,56,88,0.04);
+ // background: rgba(31,56,88,0.04);
+ border-bottom: 1px solid #EDEFF3;
+ .lc-tab-title{
+ flex: 1;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-bottom: 2px solid transparent;
+ cursor: pointer;
+ font-size: 12px;
+ &.actived {
+ color: #0079F2;
+ border-bottom-color: #0079F2;
+ }
+ }
}
.lc-tabs-content {
position: absolute;
- top: 36px;
+ top: 32px;
bottom: 0;
left: 0;
right: 0;
@@ -265,16 +354,23 @@ body {
flex-direction: column;
justify-content: flex-start;
align-items: center;
- .lc-title{
- padding: 12px;
+ .lc-title {
flex-direction: column;
- &.has-tip{
+ width: 46px;
+ height: 46px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &.has-tip {
cursor: pointer;
}
&.actived{
color: #0079F2;
}
- .lc-title-icon{
+ .lc-title-icon {
+ height: 20px;
+ width: 20px;
margin: 0;
.next-icon:before {
line-height: 1 !important;
@@ -301,8 +397,9 @@ body {
}
.lc-pane-close {
position: absolute;
- right: 10px;
- top: 6px;
+ right: 16px;
+ top: 16px;
+ height: auto;
z-index: 2;
.next-icon {
line-height: 1;
@@ -324,6 +421,7 @@ body {
flex: 1;
display: flex;
flex-direction: column;
+ z-index: 10;
.lc-toolbar {
height: var(--toolbar-height);
background-color: var(--color-pane-background);
diff --git a/packages/editor-skeleton/src/layouts/workbench.tsx b/packages/editor-skeleton/src/layouts/workbench.tsx
index af50045c4..06f0b5055 100644
--- a/packages/editor-skeleton/src/layouts/workbench.tsx
+++ b/packages/editor-skeleton/src/layouts/workbench.tsx
@@ -13,16 +13,16 @@ import RightArea from './right-area';
import './workbench.less';
@observer
-export class Workbench extends Component<{ skeleton: Skeleton, className?: string }> {
+export class Workbench extends Component<{ skeleton: Skeleton, className?: string, topAreaItemClassName?: string }> {
shouldComponentUpdate() {
return false;
}
render() {
- const { skeleton, className } = this.props;
+ const { skeleton, className, topAreaItemClassName } = this.props;
return (
-
+
diff --git a/packages/editor-skeleton/src/transducers/addon-combine.ts b/packages/editor-skeleton/src/transducers/addon-combine.ts
index c0ee2a480..6c4015d14 100644
--- a/packages/editor-skeleton/src/transducers/addon-combine.ts
+++ b/packages/editor-skeleton/src/transducers/addon-combine.ts
@@ -1,4 +1,5 @@
import { TransformedComponentMetadata, FieldConfig, SettingTarget } from '@ali/lowcode-types';
+import { IconSlot } from '../icons/slot';
export default function(metadata: TransformedComponentMetadata): TransformedComponentMetadata {
const { componentName, configure = {} } = metadata;
@@ -46,7 +47,7 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
const supportedLifecycles =
events.supportedLifecycles ||
(isRoot
- ? [
+ ? /*[
{
description: '初始化时',
name: 'constructor',
@@ -63,7 +64,7 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
description: '卸载时',
name: 'componentWillUnmount',
},
- ]
+ ]*/ null
: null);
if (supportedLifecycles) {
eventsDefinition.push({
@@ -80,7 +81,22 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
});
}
// 通用设置
- const propsGroup = props || [];
+ let propsGroup = props || [];
+ const basicInfo: any = {};
+ if (componentName === 'Slot') {
+ basicInfo.icon = IconSlot;
+ propsGroup = [{
+ name: '___title',
+ title: {
+ type: 'i18n',
+ 'en-US': 'Slot Title',
+ 'zh-CN': '插槽标题'
+ },
+ setter: 'StringSetter',
+ defaultValue: '插槽容器'
+ }]
+ }
+ /*
propsGroup.push({
name: '#generals',
title: { type: 'i18n', 'zh-CN': '通用', 'en-US': 'General' },
@@ -101,14 +117,14 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
title: 'Ref',
setter: 'StringSetter',
},
- /*
{
name: '!more',
title: '更多',
setter: 'PropertiesSetter',
- },*/
+ },
],
});
+ */
const combined: FieldConfig[] = [
{
title: { type: 'i18n', 'zh-CN': '属性', 'en-US': 'Props' },
@@ -182,8 +198,15 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
items: [
{
name: '___condition',
- title: { type: 'i18n', 'zh-CN': '条件显示', 'en-US': 'Condition' },
- setter: 'ExpressionSetter',
+ title: { type: 'i18n', 'zh-CN': '是否渲染', 'en-US': 'Condition' },
+ setter: [{
+ componentName: 'BoolSetter',
+ props: {
+ defaultValue: true,
+ }
+ }, {
+ componentName: 'VariableSetter'
+ }],
},
{
name: '#loop',
@@ -192,27 +215,14 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
{
name: '___loop',
title: { type: 'i18n', 'zh-CN': '循环数据', 'en-US': 'Loop Data' },
- setter: {
- componentName: 'MixinSetter',
+ setter: [{
+ componentName: 'JsonSetter',
props: {
- // TODO:
- setters: [
- {
- componentName: 'JSONSetter',
- props: {
- mode: 'popup',
- placeholder: { type: 'i18n', 'zh-CN': '编辑数据', 'en-US': 'Edit Data' },
- },
- },
- {
- componentName: 'ExpressionSetter',
- props: {
- placeholder: { type: 'i18n', 'zh-CN': '绑定数据', 'en-US': 'Bind Data' },
- },
- },
- ],
+ label: { type: 'i18n', 'zh-CN': '编辑数据', 'en-US': 'Edit Data'},
},
- },
+ }, {
+ componentName: 'VariableSetter'
+ }],
},
{
name: '___loopArgs.0',
@@ -236,8 +246,12 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
},
{
name: 'key',
- title: 'Key',
- setter: 'ExpressionSetter',
+ title: '循环 Key',
+ setter: [{
+ componentName: 'StringSetter',
+ }, {
+ componentName: 'VariableSetter'
+ }],
},
],
},
@@ -247,6 +261,7 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
return {
...metadata,
+ ...basicInfo,
configure: {
...configure,
combined,
diff --git a/packages/editor-skeleton/src/widget/dock.ts b/packages/editor-skeleton/src/widget/dock.ts
index c63bbdc2c..4b2081e53 100644
--- a/packages/editor-skeleton/src/widget/dock.ts
+++ b/packages/editor-skeleton/src/widget/dock.ts
@@ -45,6 +45,8 @@ export default class Dock implements IWidget {
} else {
this._body = createElement(DockView, props);
}
+ this.inited = true;
+
return this._body;
}
diff --git a/packages/editor-skeleton/src/widget/panel-dock.ts b/packages/editor-skeleton/src/widget/panel-dock.ts
index e83bbddde..d446eeba6 100644
--- a/packages/editor-skeleton/src/widget/panel-dock.ts
+++ b/packages/editor-skeleton/src/widget/panel-dock.ts
@@ -59,14 +59,14 @@ export default class PanelDock implements IWidget {
this.id = uniqueId(`dock:${name}$`);
this.panelName = config.panelName || name;
if (content) {
+ const _panelProps: any = { ...panelProps };
+ if (_panelProps.title == null && props) {
+ _panelProps.title = composeTitle(props.title, undefined, props.description, true, true);
+ }
this._panel = this.skeleton.add({
type: "Panel",
name: this.panelName,
- props: {
- // FIXME! give default title for panel
- title: props ? composeTitle(props?.title, props?.icon, props?.description, true) : '',
- ...panelProps,
- },
+ props: _panelProps,
contentProps,
content,
area: panelProps?.area
diff --git a/packages/editor-skeleton/src/widget/panel.ts b/packages/editor-skeleton/src/widget/panel.ts
index f03e6a130..ff7273683 100644
--- a/packages/editor-skeleton/src/widget/panel.ts
+++ b/packages/editor-skeleton/src/widget/panel.ts
@@ -1,3 +1,4 @@
+import { EventEmitter } from 'events';
import { createElement, ReactNode } from 'react';
import { obx } from '@ali/lowcode-editor-core';
import { uniqueId, createContent } from '@ali/lowcode-utils';
@@ -13,8 +14,9 @@ export default class Panel implements IWidget {
readonly isWidget = true;
readonly name: string;
readonly id: string;
- @obx.ref inited: boolean = false;
- @obx.ref private _actived: boolean = false;
+ @obx.ref inited = false;
+ @obx.ref private _actived = false;
+ private emitter = new EventEmitter();
get actived(): boolean {
return this._actived;
}
@@ -46,7 +48,7 @@ export default class Panel implements IWidget {
readonly title: TitleContent;
readonly help?: HelpTipConfig;
- private plain: boolean = false;
+ private plain = false;
private container?: WidgetContainer
;
private parent?: WidgetContainer;
@@ -60,6 +62,9 @@ export default class Panel implements IWidget {
this.plain = hideTitleBar || !title;
this.help = help;
if (Array.isArray(content)) {
+ if (content.length === 1) {
+ // todo: not show tabs
+ }
this.container = this.skeleton.createContainer(
name,
(item) => {
@@ -121,7 +126,11 @@ export default class Panel implements IWidget {
}
active(item?: Panel | string | null) {
- this.container?.active(item);
+ if (item) {
+ this.container?.active(item);
+ } else {
+ this.setActive(true);
+ }
}
getName() {
@@ -143,9 +152,11 @@ export default class Panel implements IWidget {
}
this._actived = true;
this.parent?.active(this);
+ this.emitter.emit('activechange', true);
} else if (this.inited) {
this._actived = false;
this.parent?.unactive(this);
+ this.emitter.emit('activechange', false);
}
}
@@ -179,6 +190,16 @@ export default class Panel implements IWidget {
* @deprecated
*/
setPosition(position: string) {
+ // noop
+ }
+ /**
+ * @deprecated
+ */
+ onActiveChange(fn: (flag: boolean) => void): () => void {
+ this.emitter.on('activechange', fn);
+ return () => {
+ this.emitter.removeListener('activechange', fn);
+ };
}
}
diff --git a/packages/editor-skeleton/src/widget/utils.ts b/packages/editor-skeleton/src/widget/utils.ts
index b635fbbe0..b90c33df8 100644
--- a/packages/editor-skeleton/src/widget/utils.ts
+++ b/packages/editor-skeleton/src/widget/utils.ts
@@ -1,7 +1,7 @@
-import { IconType, TitleContent, isI18nData, TipContent } from '@ali/lowcode-types';
+import { IconType, TitleContent, isI18nData, TipContent, isTitleConfig } from '@ali/lowcode-types';
import { isValidElement } from 'react';
-export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipContent, tipAsTitle?: boolean) {
+export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipContent, tipAsTitle?: boolean, noIcon?: boolean) {
if (!title) {
title = {};
if (!icon || tipAsTitle) {
@@ -11,6 +11,19 @@ export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipCon
}
if (icon || tip) {
if (typeof title !== 'object' || isValidElement(title) || isI18nData(title)) {
+ if (isValidElement(title)) {
+ if (title.type === 'svg' || (title.type as any).getIcon) {
+ if (!icon) {
+ icon = title as any;
+ }
+ if (tipAsTitle) {
+ title = tip as any;
+ tip = null;
+ } else {
+ title = undefined;
+ }
+ }
+ }
title = {
label: title,
icon,
@@ -24,5 +37,8 @@ export function composeTitle(title?: TitleContent, icon?: IconType, tip?: TipCon
};
}
}
+ if (isTitleConfig(title) && noIcon) {
+ title.icon = undefined;
+ }
return title;
}
diff --git a/packages/editor-skeleton/src/widget/widget-container.ts b/packages/editor-skeleton/src/widget/widget-container.ts
index 268f5fcfb..522648301 100644
--- a/packages/editor-skeleton/src/widget/widget-container.ts
+++ b/packages/editor-skeleton/src/widget/widget-container.ts
@@ -39,7 +39,7 @@ export default class WidgetContainer
-
+
{treeNode.slots.map(tnode => (
diff --git a/packages/plugin-outline-pane/src/views/tree-title.tsx b/packages/plugin-outline-pane/src/views/tree-title.tsx
index 3d7c6b373..2417102ae 100644
--- a/packages/plugin-outline-pane/src/views/tree-title.tsx
+++ b/packages/plugin-outline-pane/src/views/tree-title.tsx
@@ -5,12 +5,11 @@ import { IconArrowRight } from '../icons/arrow-right';
import { IconEyeClose } from '../icons/eye-close';
import { IconLock } from '../icons/lock';
import { IconUnlock } from '../icons/unlock';
-import { intl } from '../locale';
+import { intl, intlNode } from '../locale';
import TreeNode from '../tree-node';
import { IconEye } from '../icons/eye';
import { IconCond } from '../icons/cond';
import { IconLoop } from '../icons/loop';
-import { IconSlot } from '../icons/slot';
import { createIcon } from '@ali/lowcode-utils';
@observer
@@ -104,29 +103,28 @@ export default class TreeTitle extends Component<{
{node.slotFor && (
{/* todo: click redirect to prop */}
-
- {intl('Slot for {prop}', { prop: node.slotFor.key })}
+ {intlNode('Slot for {prop}', { prop: node.slotFor.key })}
)}
{node.hasLoop() && (
{/* todo: click todo something */}
- {intl('Loop')}
+ {intlNode('Loop')}
)}
{node.hasCondition() && !node.conditionGroup && (
{/* todo: click todo something */}
- {intl('Conditional')}
+ {intlNode('Conditional')}
)}
)}
{isCNode && isNodeParent &&
}
- {isCNode && isNodeParent &&
}
+ {/*isCNode && isNodeParent &&
*/}
);
}
diff --git a/packages/plugin-outline-pane/tsconfig.json b/packages/plugin-outline-pane/tsconfig.json
index c37b76ecc..af656bc8f 100644
--- a/packages/plugin-outline-pane/tsconfig.json
+++ b/packages/plugin-outline-pane/tsconfig.json
@@ -4,6 +4,6 @@
"outDir": "lib"
},
"include": [
- "./src/"
+ "./src/",
]
}
diff --git a/packages/react-renderer/src/engine/base.jsx b/packages/react-renderer/src/engine/base.jsx
index 6bf45fe3f..14059f015 100644
--- a/packages/react-renderer/src/engine/base.jsx
+++ b/packages/react-renderer/src/engine/base.jsx
@@ -228,6 +228,10 @@ export default class BaseEngine extends PureComponent {
let Comp = components[schema.componentName] || Div;
+ if (schema.hidden) {
+ return null;
+ }
+
if (schema.loop !== undefined) {
return this.__createLoopVirtualDom(
{
diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json
index d031eb62c..b62d92110 100644
--- a/packages/react-simulator-renderer/package.json
+++ b/packages/react-simulator-renderer/package.json
@@ -21,6 +21,7 @@
"@recore/obx-react": "^1.0.7",
"classnames": "^2.2.6",
"react": "^16",
+ "@ali/vu-css-style": "^1.0.2",
"react-dom": "^16.7.0"
},
"devDependencies": {
diff --git a/packages/react-simulator-renderer/src/builtin-components/slot.tsx b/packages/react-simulator-renderer/src/builtin-components/slot.tsx
index 107864d52..cf67dbcf2 100644
--- a/packages/react-simulator-renderer/src/builtin-components/slot.tsx
+++ b/packages/react-simulator-renderer/src/builtin-components/slot.tsx
@@ -6,7 +6,7 @@ class Slot extends Component {
componentName: 'Slot',
configure: {
props: [{
- name: '___title___',
+ name: '___title',
title: {
type: 'i18n',
'en-US': 'Slot Title',
@@ -15,7 +15,7 @@ class Slot extends Component {
setter: 'StringSetter',
defaultValue: '插槽容器'
}, {
- name: '___params___',
+ name: '___params',
title: {
type: 'i18n',
'en-US': 'Slot Params',
diff --git a/packages/react-simulator-renderer/src/renderer-view.tsx b/packages/react-simulator-renderer/src/renderer-view.tsx
index 7b41312c8..5ae9ce0fe 100644
--- a/packages/react-simulator-renderer/src/renderer-view.tsx
+++ b/packages/react-simulator-renderer/src/renderer-view.tsx
@@ -1,4 +1,5 @@
import LowCodeRenderer from '@ali/lowcode-react-renderer';
+import { isObject } from 'lodash';
import { ReactInstance, Fragment, Component, createElement } from 'react';
import { observer } from '@recore/obx-react';
import { SimulatorRenderer } from './renderer';
@@ -7,7 +8,7 @@ import './renderer.less';
// patch cloneElement avoid lost keyProps
const originCloneElement = window.React.cloneElement;
-(window as any).React.cloneElement = (child: any, { _leaf, ...props}: any = {}) => {
+(window as any).React.cloneElement = (child: any, { _leaf, ...props }: any = {}) => {
if (child.ref && props.ref) {
const dRef = props.ref;
const cRef = child.ref;
@@ -18,7 +19,7 @@ const originCloneElement = window.React.cloneElement;
} else {
try {
cRef.current = x;
- } catch (e) { }
+ } catch (e) {}
}
}
if (dRef) {
@@ -27,13 +28,13 @@ const originCloneElement = window.React.cloneElement;
} else {
try {
dRef.current = x;
- } catch (e) { }
+ } catch (e) {}
}
}
- }
- };
+ };
+ }
return originCloneElement(child, props);
-}
+};
export default class SimulatorRendererView extends Component<{ renderer: SimulatorRenderer }> {
render() {
@@ -84,10 +85,11 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
const { __id, __desingMode, ...viewProps } = props;
viewProps.componentId = __id;
viewProps._leaf = host.document.getNode(__id);
+
return createElement(
Component,
viewProps,
- children == null ? null : Array.isArray(children) ? children : [children],
+ children == null ? [] : Array.isArray(children) ? children : [children],
);
}}
onCompGetRef={(schema: any, ref: ReactInstance | null) => {
diff --git a/packages/types/src/title.ts b/packages/types/src/title.ts
index 3be21d84e..7a260ebeb 100644
--- a/packages/types/src/title.ts
+++ b/packages/types/src/title.ts
@@ -1,5 +1,5 @@
import { ReactElement, ReactNode } from 'react';
-import { I18nData } from './i18n';
+import { I18nData, isI18nData } from './i18n';
import { TipContent } from './tip';
import { IconType } from './icon';
@@ -13,3 +13,14 @@ export interface TitleConfig {
export type TitleContent = string | I18nData | ReactElement | TitleConfig;
+function isPlainObject(value: any): value is object {
+ if (typeof value !== 'object') {
+ return false;
+ }
+ const proto = Object.getPrototypeOf(value);
+ return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null;
+}
+
+export function isTitleConfig(obj: any): obj is TitleConfig {
+ return isPlainObject(obj) && !isI18nData(obj);
+}
diff --git a/packages/utils/src/is-plain-object.ts b/packages/utils/src/is-plain-object.ts
index 899ec5c64..59260d6cb 100644
--- a/packages/utils/src/is-plain-object.ts
+++ b/packages/utils/src/is-plain-object.ts
@@ -1,6 +1,6 @@
import { isObject } from './is-object';
-export function isPlainObject(value: any) {
+export function isPlainObject(value: any): value is object {
if (!isObject(value)) {
return false;
}
diff --git a/packages/vision-preset/src/bundle/bundle.ts b/packages/vision-preset/src/bundle/bundle.ts
index 6e00f7e58..d74c3de12 100644
--- a/packages/vision-preset/src/bundle/bundle.ts
+++ b/packages/vision-preset/src/bundle/bundle.ts
@@ -100,6 +100,22 @@ export default class Bundle {
cp.setView(view);
}
+ /**
+ * TODO dirty fix
+ */
+ addComponentBundle(bundles: any) {
+ /**
+ * Normal Component bundle: [ Prototype, PrototypeView ]
+ * Component without Prototype.js: [ View ]
+ */
+ if (bundles.length >= 2) {
+ const prototype = bundles[0];
+ const prototypeView = bundles[1];
+ prototype.setView(prototypeView);
+ this.registerPrototype(prototype);
+ }
+ }
+
private recursivelyRegisterViews(list: any[], viewName?: string): void {
list.forEach((item: any) => {
if (Array.isArray(item.module)) {
diff --git a/packages/vision-preset/src/bundle/upgrade-metadata.ts b/packages/vision-preset/src/bundle/upgrade-metadata.ts
index 3ea38d90e..b439559a4 100644
--- a/packages/vision-preset/src/bundle/upgrade-metadata.ts
+++ b/packages/vision-preset/src/bundle/upgrade-metadata.ts
@@ -237,7 +237,7 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
}
}
- if (collapse || collapsed || fieldCollapsed) {
+ if (collapse || collapsed || fieldCollapsed || extraProps.display === DISPLAY_TYPE.ENTRY) {
extraProps.defaultCollapsed = true;
}
function isDisabled(field: Field) {
@@ -287,10 +287,14 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
if (slotName && initialValue === true) {
initialFn = (field: any, value: any) => {
if (isJSSlot(value)) {
- return value;
+ return {
+ title: slotTitle || title,
+ ...value,
+ };
}
return {
type: 'JSSlot',
+ title: slotTitle || title,
value: initialChildren,
};
};
@@ -351,10 +355,14 @@ export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial)
componentName: 'SlotSetter',
initialValue: (field: any, value: any) => {
if (isJSSlot(value)) {
- return value;
+ return {
+ title: slotTitle || title,
+ ...value,
+ };
}
return {
type: 'JSSlot',
+ title: slotTitle || title,
value: value == null ? initialChildren : value,
};
},
diff --git a/packages/vision-preset/src/components/index.less b/packages/vision-preset/src/components/index.less
new file mode 100644
index 000000000..c630e0914
--- /dev/null
+++ b/packages/vision-preset/src/components/index.less
@@ -0,0 +1,82 @@
+@import '~@ali/ve-less-variables/index.less';
+
+// 样式直接沿用之前的样式,优化了下命名
+.instance-node-selector {
+ position: relative;
+ margin-right: 2px;
+ color: var(--color-icon-white, @title-bgcolor);
+ border-radius: @global-border-radius;
+ margin-right: 2px;
+ pointer-events: auto;
+ flex-grow: 0;
+ flex-shrink: 0;
+
+ svg {
+ width: 16px;
+ height: 16px;
+ margin-right: 5px;
+ flex-grow: 0;
+ flex-shrink: 0;
+ max-width: inherit;
+ path {
+ fill: var(--color-icon-white, @title-bgcolor);
+ }
+ }
+ &-current {
+ background: var(--color-brand, @brand-color-1);
+ padding: 0 6px;
+ display: flex;
+ align-items: center;
+ height: 20px;
+ cursor: pointer;
+ color: var(--color-icon-white, @title-bgcolor);
+ border-radius: 3px;
+
+ &-title {
+ padding-right: 6px;
+ color: var(--color-icon-white, @title-bgcolor);
+ }
+ }
+ &-list {
+ position: absolute;
+ left: 0;
+ right: 0;
+ opacity: 0;
+ visibility: hidden;
+ }
+ &-node {
+ margin: 2px 0;
+ &-content {
+ padding-left: 6px;
+ background: #78869a;
+ display: inline-flex;
+ border-radius: 3px;
+ align-items: center;
+ height: 20px;
+ color: var(--color-icon-white, @title-bgcolor);
+ cursor: pointer;
+ overflow: visible;
+ }
+ &-title {
+ padding-right: 6px;
+ // margin-left: 5px;
+ color: var(--color-icon-white, @title-bgcolor);
+ cursor: pointer;
+ overflow: visible;
+ }
+ &:hover {
+ opacity: 0.8;
+ }
+ }
+}
+
+&:hover {
+ .instance-node-selector-current {
+ color: ar(--color-text-reverse, @white-alpha-2);
+ }
+ .instance-node-selector-popup {
+ visibility: visible;
+ opacity: 1;
+ transition: 0.2s all ease-in;
+ }
+}
diff --git a/packages/vision-preset/src/components/index.tsx b/packages/vision-preset/src/components/index.tsx
new file mode 100644
index 000000000..f32681dd0
--- /dev/null
+++ b/packages/vision-preset/src/components/index.tsx
@@ -0,0 +1,110 @@
+import { Overlay } from '@alifd/next';
+import React from 'react';
+import './index.less';
+import { Title } from '@ali/lowcode-editor-core';
+
+import { Node, ParentalNode } from '@ali/lowcode-designer';
+
+const { Popup } = Overlay;
+
+export interface IProps {
+ node: Node;
+}
+
+export interface IState {
+ parentNodes: Node[];
+}
+
+type UnionNode = Node | ParentalNode | null;
+
+export class InstanceNodeSelector extends React.Component
{
+ state: IState = {
+ parentNodes: [],
+ };
+
+ componentDidMount() {
+ const parentNodes = this.getParentNodes(this.props.node);
+ this.setState({
+ parentNodes,
+ });
+ }
+
+ // 获取节点的父级节点(最多获取5层)
+ getParentNodes = (node: Node) => {
+ const parentNodes = [];
+ let currentNode: UnionNode = node;
+
+ while (currentNode && parentNodes.length < 5) {
+ currentNode = currentNode.getParent();
+ if (currentNode) {
+ parentNodes.push(currentNode);
+ }
+ }
+ return parentNodes;
+ };
+
+ onSelect = (node: Node) => () => {
+ if (node && typeof node.select === 'function') {
+ node.select();
+ }
+ };
+ onMouseOver = (node: Node) => (_: any, flag = true) => {
+ if (node && typeof node.hover === 'function') {
+ node.hover(flag);
+ }
+ };
+ onMouseOut = (node: Node) => (_: any, flag = false) => {
+ if (node && typeof node.hover === 'function') {
+ node.hover(flag);
+ }
+ };
+ renderNodes = (node: Node) => {
+ const nodes = this.state.parentNodes || [];
+ const children = nodes.map((node, key) => {
+ return (
+
+ );
+ });
+ return children;
+ };
+
+ render() {
+ const { node } = this.props;
+ return (
+
+ }
+ triggerType="hover"
+ >
+ {this.renderNodes(node)}
+
+
+ );
+ }
+}
diff --git a/packages/vision-preset/src/editor.ts b/packages/vision-preset/src/editor.ts
index 48c6e4def..5dac4dfd4 100644
--- a/packages/vision-preset/src/editor.ts
+++ b/packages/vision-preset/src/editor.ts
@@ -1,15 +1,16 @@
import { isJSBlock, isJSSlot } from '@ali/lowcode-types';
import { isPlainObject } from '@ali/lowcode-utils';
-import { globalContext, Editor, registerSetter } from '@ali/lowcode-editor-core';
-import { Designer, TransformStage } from '@ali/lowcode-designer';
+import { globalContext, Editor } from '@ali/lowcode-editor-core';
+import { Designer, TransformStage, addBuiltinComponentAction } from '@ali/lowcode-designer';
// import { registerSetters } from '@ali/lowcode-setters';
import Outline from '@ali/lowcode-plugin-outline-pane';
+import { toCss } from '@ali/vu-css-style';
+
import DesignerPlugin from '@ali/lowcode-plugin-designer';
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
-import Preview from '@ali/lowcode-plugin-sample-preview';
-// import SourceEditor from '@ali/lowcode-plugin-source-editor';
import { i18nReducer } from './i18n-reducer';
+import { InstanceNodeSelector } from './components';
export const editor = new Editor();
globalContext.register(editor, Editor);
@@ -66,6 +67,37 @@ function upgradePropsReducer(props: any) {
}
designer.addPropsReducer(upgradePropsReducer, TransformStage.Init);
+// 设计器组件样式处理
+function stylePropsReducer(props: any, node: any) {
+ if (props && typeof props === 'object' && props.__style__) {
+ const doc = designer.currentDocument?.simulator?.contentDocument;
+ if (!doc) {
+ return;
+ }
+ const cssId = '_style_pesudo_' + node.id.replace(/\$/g, '_');
+ const cssClass = '_css_pesudo_' + node.id.replace(/\$/g, '_');
+ const dom = doc.getElementById(cssId);
+ if (dom) {
+ dom.parentNode?.removeChild(dom);
+ }
+ let styleProp = props.__style__;
+ if (typeof styleProp === 'object') {
+ styleProp = toCss(styleProp);
+ }
+ if (typeof styleProp === 'string') {
+ const s = doc.createElement('style');
+ props.className = cssClass;
+ s.setAttribute('type', 'text/css');
+ s.setAttribute('id', cssId);
+ doc.getElementsByTagName('head')[0].appendChild(s);
+
+ s.appendChild(doc.createTextNode(styleProp.replace(/:root/g, '.' + cssClass)));
+ }
+ }
+ return props;
+}
+designer.addPropsReducer(stylePropsReducer, TransformStage.Render);
+
skeleton.add({
area: 'mainArea',
name: 'designer',
@@ -88,16 +120,6 @@ skeleton.add({
},
});
-skeleton.add({
- area: 'topArea',
- type: 'Dock',
- name: 'preview',
- props: {
- align: 'right',
- },
- content: Preview,
-});
-
// skeleton.add({
// name: 'sourceEditor',
// type: 'PanelDock',
@@ -112,3 +134,11 @@ skeleton.add({
// },
// content: SourceEditor,
// });
+
+// 实例节点选择器,线框高亮
+addBuiltinComponentAction({
+ name: 'instance-node-selector',
+ content: InstanceNodeSelector,
+ important: true,
+ condition: 'always'
+});
diff --git a/packages/vision-preset/src/env.ts b/packages/vision-preset/src/env.ts
index bd5ab894a..8c7eae39b 100644
--- a/packages/vision-preset/src/env.ts
+++ b/packages/vision-preset/src/env.ts
@@ -1,13 +1,13 @@
import { EventEmitter } from 'events';
import { ALI_SCHEMA_VERSION } from './base/const';
+import { obx } from '@ali/lowcode-editor-core';
interface ILiteralObject {
[key: string]: any;
}
export class Env {
-
- public envs: ILiteralObject;
+ @obx.val envs: ILiteralObject = {};
private emitter: EventEmitter;
private featureMap: ILiteralObject;
@@ -15,23 +15,22 @@ export class Env {
constructor() {
this.emitter = new EventEmitter();
this.emitter.setMaxListeners(0);
- this.envs = {};
this.featureMap = {};
}
- public get(name: string): any {
+ get(name: string): any {
return this.getEnv(name);
}
- public getEnv(name: string): any {
+ getEnv(name: string): any {
return this.envs[name];
}
- public set(name: string, value: any) {
+ set(name: string, value: any) {
return this.setEnv(name, value);
}
- public setEnv(name: string, value: any) {
+ setEnv(name: string, value: any) {
const orig = this.envs[name];
if (JSON.stringify(orig) === JSON.stringify(value)) {
return;
@@ -40,47 +39,47 @@ export class Env {
this.emitter.emit('envchange', this.envs, name, value);
}
- public setEnvMap(envs: ILiteralObject): void {
+ setEnvMap(envs: ILiteralObject): void {
this.envs = Object.assign(this.envs, envs);
this.emitter.emit('envchange', this.envs);
}
- public getLocale(): string {
+ getLocale(): string {
return this.getEnv('locale') || 'zh_CN';
}
- public setLocale(locale: string) {
+ setLocale(locale: string) {
this.setEnv('locale', locale);
}
- public setExpertMode(flag: string) {
+ setExpertMode(flag: string) {
this.setEnv('expertMode', !!flag);
}
- public isExpertMode() {
+ isExpertMode() {
return !!this.getEnv('expertMode');
}
- public getSupportFeatures() {
+ getSupportFeatures() {
return Object.assign({}, this.featureMap);
}
- public setSupportFeatures(features: ILiteralObject) {
+ setSupportFeatures(features: ILiteralObject) {
this.featureMap = Object.assign({}, this.featureMap, features);
}
- public supports(name = 'supports') {
+ supports(name = 'supports') {
return !!this.featureMap[name];
}
- public onEnvChange(func: (envs: ILiteralObject, name: string, value: any) => any) {
+ onEnvChange(func: (envs: ILiteralObject, name: string, value: any) => any) {
this.emitter.on('envchange', func);
return () => {
this.emitter.removeListener('envchange', func);
};
}
- public getAliSchemaVersion() {
+ getAliSchemaVersion() {
return ALI_SCHEMA_VERSION;
}
}
diff --git a/packages/vision-preset/src/exchange.ts b/packages/vision-preset/src/exchange.ts
index df71cff14..6e1f8c13e 100644
--- a/packages/vision-preset/src/exchange.ts
+++ b/packages/vision-preset/src/exchange.ts
@@ -12,4 +12,13 @@ export default {
const nodes = designer.currentSelection?.getNodes();
return nodes?.[0];
},
+ /**
+ * TODO dirty fix
+ */
+ onIntoView(func: (node: any, insertion: any) => any) {
+ // this.emitter.on('intoview', func);
+ return () => {
+ // this.emitter.removeListener('intoview', func);
+ };
+ }
}
diff --git a/packages/vision-preset/src/i18n-reducer.ts b/packages/vision-preset/src/i18n-reducer.ts
index 693ee0cae..a1a153932 100644
--- a/packages/vision-preset/src/i18n-reducer.ts
+++ b/packages/vision-preset/src/i18n-reducer.ts
@@ -1,5 +1,5 @@
-const I18nUtil = require('@ali/ve-i18n-util');
import Env from './env';
+const I18nUtil = require('@ali/ve-i18n-util');
interface I18nObject {
type?: string;
@@ -9,7 +9,9 @@ interface I18nObject {
}
export function i18nReducer(obj?: any): any {
- if (!obj) { return obj; }
+ if (!obj) {
+ return obj;
+ }
if (Array.isArray(obj)) {
return obj.map((item) => i18nReducer(item));
}
@@ -18,6 +20,7 @@ export function i18nReducer(obj?: any): any {
// FIXME! use editor.get
let locale = Env.getLocale();
if (obj.key) {
+ // FIXME: 此处需要升级I18nUtil,改成响应式
return I18nUtil.get(obj.key, locale);
}
if (locale !== 'zh_CN' && locale !== 'zh_TW' && !obj[locale]) {
diff --git a/packages/vision-preset/src/index.ts b/packages/vision-preset/src/index.ts
index 2c4db40e9..ed632b3ef 100644
--- a/packages/vision-preset/src/index.ts
+++ b/packages/vision-preset/src/index.ts
@@ -1,11 +1,12 @@
import * as utils from '@ali/ve-utils';
import Popup from '@ali/ve-popups';
import Icons from '@ali/ve-icons';
+import logger from '@ali/vu-logger';
import { render } from 'react-dom';
import I18nUtil from '@ali/ve-i18n-util';
import { hotkey as Hotkey } from '@ali/lowcode-editor-core';
import { createElement } from 'react';
-import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './base/const';
+import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS, VERSION as Version } from './base/const';
import Bus from './bus';
import { skeleton } from './editor';
import { Workbench } from '@ali/lowcode-editor-skeleton';
@@ -21,7 +22,10 @@ import * as Field from './fields';
import Prop from './prop';
import Env from './env';
import DragEngine from './drag-engine';
+import Viewport from './viewport';
+import Project from './project';
import { designer, editor } from './editor';
+import Symbols from './symbols';
import './vision.less';
@@ -41,6 +45,7 @@ function init(container?: Element) {
createElement(Workbench, {
skeleton,
className: 'engine-main',
+ topAreaItemClassName: 'engine-actionitem',
}),
container,
);
@@ -101,6 +106,11 @@ const VisualEngine = {
Bundle,
Pages,
DragEngine,
+ Viewport,
+ Version,
+ Project,
+ logger,
+ Symbols,
};
(window as any).VisualEngine = VisualEngine;
@@ -144,6 +154,11 @@ export {
Bundle,
Pages,
DragEngine,
+ Viewport,
+ Version,
+ Project,
+ logger,
+ Symbols,
};
diff --git a/packages/vision-preset/src/pages.ts b/packages/vision-preset/src/pages.ts
index 81430a4f1..c269f2471 100644
--- a/packages/vision-preset/src/pages.ts
+++ b/packages/vision-preset/src/pages.ts
@@ -6,17 +6,28 @@ const { project } = designer;
export interface OldPageData {
id: string;
- layout: RootSchema;
+ componentsTree: RootSchema[];
[dataAddon: string]: any;
}
const pages = Object.assign(project, {
setPages(pages: OldPageData[]) {
+ if (!pages || !Array.isArray(pages) || pages.length === 0) {
+ throw new Error('pages schema 不合法');
+ }
+
+ if (pages[0].componentsTree[0]) {
+ pages[0].componentsTree[0].componentName = 'Page';
+ // FIXME
+ pages[0].componentsTree[0].lifeCycles = {};
+ pages[0].componentsTree[0].methods = {};
+ }
+
project.load({
version: '1.0.0',
componentsMap: [],
- componentsTree: pages.map(page => page.layout),
- });
+ componentsTree: pages[0].componentsTree,
+ }, true);
},
addPage(data: OldPageData) {
return project.open(data.layout);
diff --git a/packages/vision-preset/src/panes.ts b/packages/vision-preset/src/panes.ts
index 3604e6186..3be27f531 100644
--- a/packages/vision-preset/src/panes.ts
+++ b/packages/vision-preset/src/panes.ts
@@ -69,17 +69,7 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin
newConfig.type = 'PanelDock';
newConfig.area = 'left';
newConfig.props.description = description || title;
- const {
- contents,
- hideTitleBar,
- tip,
- width,
- maxWidth,
- height,
- maxHeight,
- menu,
- isAction
- } = config;
+ const { contents, hideTitleBar, tip, width, maxWidth, height, maxHeight, menu, isAction } = config;
if (menu) {
newConfig.props.title = menu;
}
@@ -95,15 +85,16 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin
};
if (contents && Array.isArray(contents)) {
- newConfig.content = contents.map(({ title, content, tip }) => {
+ newConfig.content = contents.map(({ title, content, tip }, index) => {
return {
- type: "Panel",
+ type: 'Panel',
+ name: typeof title === 'string' ? title : `${name}:${index}`,
content,
props: {
title,
help: tip,
- }
- }
+ },
+ };
});
}
}
@@ -162,7 +153,11 @@ const dockPane = Object.assign(skeleton.leftArea, {
return;
}
const name = item.name || item;
- skeleton.getPanel(name)?.active();
+ const pane = skeleton.getPanel(name);
+ if (!pane) {
+ console.warn(`Could not find pane with name ${name}`);
+ }
+ pane?.active();
},
/**
diff --git a/packages/vision-preset/src/project.ts b/packages/vision-preset/src/project.ts
new file mode 100644
index 000000000..ba5d2309b
--- /dev/null
+++ b/packages/vision-preset/src/project.ts
@@ -0,0 +1,17 @@
+class Project {
+ private schema: any;
+
+ constructor() {
+ this.schema = {};
+ }
+
+ getSchema() {
+ return this.schema;
+ }
+
+ setSchema(schema: any) {
+ this.schema = schema;
+ }
+}
+
+export default new Project();
diff --git a/packages/vision-preset/src/prop.ts b/packages/vision-preset/src/prop.ts
index 478fe0ec2..8d9abdfbf 100644
--- a/packages/vision-preset/src/prop.ts
+++ b/packages/vision-preset/src/prop.ts
@@ -2,11 +2,12 @@ import { Component } from 'react';
import { EventEmitter } from 'events';
import { fromJS, Iterable, Map as IMMap } from 'immutable';
import logger from '@ali/vu-logger';
-import { uniqueId, cloneDeep, isDataEqual, combineInitial, Transducer } from '@ali/ve-utils';
+import { cloneDeep, isDataEqual, combineInitial, Transducer } from '@ali/ve-utils';
import I18nUtil from '@ali/ve-i18n-util';
import { getSetter } from '@ali/lowcode-editor-core';
import { editor } from './editor';
import { OldPropConfig, DISPLAY_TYPE } from './bundle/upgrade-metadata';
+import { uniqueId } from '@ali/lowcode-utils';
type IPropConfig = OldPropConfig;
@@ -108,7 +109,7 @@ export default class Prop implements IVariableSettable {
this.parent = parent;
}
- this.id = uniqueId(null as any, 'prop', 'engine-prop');
+ this.id = uniqueId('prop');
if (typeof config.setter === 'string') {
config.setter = getSetter(config.setter)?.component as any;
diff --git a/packages/vision-preset/src/symbols.ts b/packages/vision-preset/src/symbols.ts
new file mode 100644
index 000000000..81091e371
--- /dev/null
+++ b/packages/vision-preset/src/symbols.ts
@@ -0,0 +1,17 @@
+export class SymbolManager {
+ private symbolMap: { [symbolName: string]: symbol } = {};
+
+ public create(name: string): symbol {
+ if (this.symbolMap[name]) {
+ return this.symbolMap[name];
+ }
+ this.symbolMap[name] = Symbol(name);
+ return this.symbolMap[name];
+ }
+
+ public get(name: string) {
+ return this.symbolMap[name];
+ }
+}
+
+export default new SymbolManager();
diff --git a/packages/vision-preset/src/viewport.ts b/packages/vision-preset/src/viewport.ts
new file mode 100644
index 000000000..0b5ec7cb7
--- /dev/null
+++ b/packages/vision-preset/src/viewport.ts
@@ -0,0 +1,278 @@
+import { EventEmitter } from 'events';
+
+const domReady = require('domready');
+import Flags from './flags';
+
+function enterFullscreen() {
+ const elem = document.documentElement;
+ if (elem.requestFullscreen) {
+ elem.requestFullscreen();
+ }
+}
+
+function exitFullscreen() {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ }
+}
+
+function isFullscreen() {
+ return document.fullscreen || false;
+}
+
+interface IStyleResourceConfig {
+ media?: string;
+ type?: string;
+ content?: string;
+}
+
+class StyleResource {
+ config: IStyleResourceConfig;
+ styleElement: HTMLStyleElement;
+ mounted: boolean;
+ inited: boolean;
+
+ constructor(config: IStyleResourceConfig) {
+ this.config = config || {};
+ }
+
+ matchDevice(device: string) {
+ const media = this.config.media;
+
+ if (!media || media === 'ALL' || media === '*') {
+ return true;
+ }
+
+ return media.toUpperCase() === device.toUpperCase();
+ }
+
+ init() {
+ if (this.inited) {
+ return;
+ }
+
+ this.inited = true;
+
+ const { type, content } = this.config;
+
+ let styleElement;
+ if (type === 'URL') {
+ styleElement = document.createElement('link');
+ styleElement.href = content || '';
+ styleElement.rel = 'stylesheet';
+ } else {
+ styleElement = document.createElement('style');
+ styleElement.setAttribute('type', 'text/css');
+ if (styleElement.styleSheet) {
+ styleElement.styleSheet.cssText = content;
+ } else {
+ styleElement.appendChild(document.createTextNode(content || ''));
+ }
+ }
+ this.styleElement = styleElement;
+ }
+
+ apply() {
+ if (this.mounted) {
+ return;
+ }
+
+ this.init();
+ document.head.appendChild(this.styleElement);
+ this.mounted = true;
+ }
+
+ unmount() {
+ if (!this.mounted) {
+ return;
+ }
+ document.head.removeChild(this.styleElement);
+ this.mounted = false;
+ }
+}
+
+export class Viewport {
+ preview: boolean;
+ focused: boolean;
+ slateFixed: boolean;
+ emitter: EventEmitter;
+ device: string;
+ focusTarget: any;
+ cssResourceSet: StyleResource[];
+
+ constructor() {
+ this.preview = false;
+ this.emitter = new EventEmitter();
+ document.addEventListener('webkitfullscreenchange', () => {
+ this.emitter.emit('fullscreenchange', this.isFullscreen());
+ });
+ domReady(() => this.applyMediaCSS());
+ }
+
+ setFullscreen(flag: boolean) {
+ const fullscreen = this.isFullscreen();
+ if (fullscreen && !flag) {
+ exitFullscreen();
+ } else if (!fullscreen && flag) {
+ enterFullscreen();
+ }
+ }
+
+ toggleFullscreen() {
+ if (this.isFullscreen()) {
+ exitFullscreen();
+ } else {
+ enterFullscreen();
+ }
+ }
+
+ isFullscreen() {
+ return isFullscreen();
+ }
+
+ setFocus(flag: boolean) {
+ if (this.focused && !flag) {
+ this.focused = false;
+ Flags.remove('view-focused');
+ this.emitter.emit('focuschange', false);
+ } else if (!this.focused && flag) {
+ this.focused = true;
+ Flags.add('view-focused');
+ this.emitter.emit('focuschange', true);
+ }
+ }
+
+ setFocusTarget(focusTarget: any) {
+ this.focusTarget = focusTarget;
+ }
+
+ returnFocus() {
+ if (this.focusTarget) {
+ this.focusTarget.focus();
+ }
+ }
+
+ isFocus() {
+ return this.focused;
+ }
+
+ setPreview(flag: boolean) {
+ if (this.preview && !flag) {
+ this.preview = false;
+ Flags.setPreviewMode(false);
+ this.emitter.emit('preview', false);
+ this.changeViewport();
+ } else if (!this.preview && flag) {
+ this.preview = true;
+ Flags.setPreviewMode(true);
+ this.emitter.emit('preview', true);
+ this.changeViewport();
+ }
+ }
+
+ togglePreview() {
+ if (this.isPreview()) {
+ this.setPreview(false);
+ } else {
+ this.setPreview(true);
+ }
+ }
+
+ isPreview() {
+ return this.preview;
+ }
+
+ setDevice(device = 'pc') {
+ if (this.getDevice() !== device) {
+ this.device = device;
+ Flags.setSimulator(device);
+ this.applyMediaCSS();
+ this.emitter.emit('devicechange', device);
+ this.changeViewport();
+ }
+ }
+
+ getDevice() {
+ return this.device || 'pc';
+ }
+
+ changeViewport() {
+ this.emitter.emit('viewportchange', this.getViewport());
+ }
+
+ getViewport() {
+ return `${this.isPreview() ? 'preview' : 'design'}-${this.getDevice()}`;
+ }
+
+ applyMediaCSS() {
+ if (!document.head || !this.cssResourceSet) {
+ return;
+ }
+ const device = this.getDevice();
+ this.cssResourceSet.forEach((item) => {
+ if (item.matchDevice(device)) {
+ item.apply();
+ } else {
+ item.unmount();
+ }
+ });
+ }
+
+ setGlobalCSS(resourceSet: IStyleResourceConfig[]) {
+ if (this.cssResourceSet) {
+ this.cssResourceSet.forEach((item) => {
+ item.unmount();
+ });
+ }
+ this.cssResourceSet = resourceSet.map((item: IStyleResourceConfig) => new StyleResource(item)).reverse();
+ this.applyMediaCSS();
+ }
+
+ setWithShell(shell: string) {
+ Flags.setWithShell(shell);
+ }
+
+ onFullscreenChange(func: () => any) {
+ this.emitter.on('fullscreenchange', func);
+ return () => {
+ this.emitter.removeListener('fullscreenchange', func);
+ };
+ }
+
+ onPreview(func: () => any) {
+ this.emitter.on('preview', func);
+ return () => {
+ this.emitter.removeListener('preview', func);
+ };
+ }
+
+ onDeviceChange(func: () => any) {
+ this.emitter.on('devicechange', func);
+ return () => {
+ this.emitter.removeListener('devicechange', func);
+ };
+ }
+
+ onSlateFixedChange(func: (flag: boolean) => any) {
+ this.emitter.on('slatefixed', func);
+ return () => {
+ this.emitter.removeListener('slatefixed', func);
+ };
+ }
+
+ onViewportChange(func: () => any) {
+ this.emitter.on('viewportchange', func);
+ return () => {
+ this.emitter.removeListener('viewportchange', func);
+ };
+ }
+
+ onFocusChange(func: (flag: boolean) => any) {
+ this.emitter.on('focuschange', func);
+ return () => {
+ this.emitter.removeListener('focuschange', func);
+ };
+ }
+}
+
+export default new Viewport();
diff --git a/packages/vision-preset/src/vision.less b/packages/vision-preset/src/vision.less
index d0c17fcc0..d188878a5 100644
--- a/packages/vision-preset/src/vision.less
+++ b/packages/vision-preset/src/vision.less
@@ -92,7 +92,8 @@ html.engine-blur #engine {
.next-checkbox-group,.next-date-picker,.next-input,.next-month-picker,
.next-number-picker,.next-radio-group,.next-range,.next-range-picker,
.next-rating,.next-select,.next-switch,.next-time-picker,.next-upload,
- .next-year-picker {
+ .next-year-picker,
+ .next-breadcrumb-item,.next-calendar-header,.next-calendar-table {
pointer-events: auto !important;
}
}
@@ -100,3 +101,9 @@ html.engine-blur #engine {
.lc-left-float-pane {
font-size: 14px;
}
+
+html.engine-preview-mode {
+ .lc-left-area, .lc-right-area {
+ display: none !important;
+ }
+}