mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-19 05:48:17 +00:00
Merge branch 'polyfill/vision' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into polyfill/vision
This commit is contained in:
commit
b99f1e5f4d
@ -7,6 +7,8 @@ import {
|
|||||||
TitleContent,
|
TitleContent,
|
||||||
TransformedComponentMetadata,
|
TransformedComponentMetadata,
|
||||||
NestingFilter,
|
NestingFilter,
|
||||||
|
isTitleConfig,
|
||||||
|
I18nData,
|
||||||
} from '@ali/lowcode-types';
|
} from '@ali/lowcode-types';
|
||||||
import { computed } from '@ali/lowcode-editor-core';
|
import { computed } from '@ali/lowcode-editor-core';
|
||||||
import { Node, ParentalNode } from './document';
|
import { Node, ParentalNode } from './document';
|
||||||
@ -17,6 +19,7 @@ import { IconPage } from './icons/page';
|
|||||||
import { IconComponent } from './icons/component';
|
import { IconComponent } from './icons/component';
|
||||||
import { IconRemove } from './icons/remove';
|
import { IconRemove } from './icons/remove';
|
||||||
import { IconClone } from './icons/clone';
|
import { IconClone } from './icons/clone';
|
||||||
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
function ensureAList(list?: string | string[]): string[] | null {
|
function ensureAList(list?: string | string[]): string[] | null {
|
||||||
if (!list) {
|
if (!list) {
|
||||||
@ -91,12 +94,20 @@ export class ComponentMeta {
|
|||||||
private childWhitelist?: NestingFilter | null;
|
private childWhitelist?: NestingFilter | null;
|
||||||
|
|
||||||
private _title?: TitleContent;
|
private _title?: TitleContent;
|
||||||
get title() {
|
get title(): string | I18nData | ReactElement {
|
||||||
|
// TODO: 标记下。这块需要康师傅加一下API,页面正常渲染。
|
||||||
|
// string | i18nData | ReactElement
|
||||||
|
// TitleConfig title.label
|
||||||
|
if (isTitleConfig(this._title)) {
|
||||||
|
return (this._title.label as any) || this.componentName;
|
||||||
|
}
|
||||||
return this._title || this.componentName;
|
return this._title || this.componentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get icon() {
|
@computed get icon() {
|
||||||
|
// TODO: 标记下。这块需要康师傅加一下API,页面正常渲染。
|
||||||
// give Slot default icon
|
// give Slot default icon
|
||||||
|
// if _title is TitleConfig get _title.icon
|
||||||
return (
|
return (
|
||||||
this._transformedMetadata?.icon ||
|
this._transformedMetadata?.icon ||
|
||||||
(this.componentName === 'Page' ? IconPage : this.isContainer ? IconContainer : IconComponent)
|
(this.componentName === 'Page' ? IconPage : this.isContainer ? IconContainer : IconComponent)
|
||||||
|
|||||||
@ -180,6 +180,11 @@ export class SettingPropEntry implements SettingEntry {
|
|||||||
return this.top;
|
return this.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add settingfield props
|
||||||
|
get props() {
|
||||||
|
return this.top;
|
||||||
|
}
|
||||||
|
|
||||||
onValueChange(func: () => any) {
|
onValueChange(func: () => any) {
|
||||||
this.emitter.on('valuechange', func);
|
this.emitter.on('valuechange', func);
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
PropsList,
|
PropsList,
|
||||||
NodeData,
|
NodeData,
|
||||||
TitleContent,
|
TitleContent,
|
||||||
|
I18nData,
|
||||||
SlotSchema,
|
SlotSchema,
|
||||||
PageSchema,
|
PageSchema,
|
||||||
ComponentSchema,
|
ComponentSchema,
|
||||||
@ -19,6 +20,7 @@ import { Prop } from './props/prop';
|
|||||||
import { ComponentMeta } from '../../component-meta';
|
import { ComponentMeta } from '../../component-meta';
|
||||||
import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group';
|
import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group';
|
||||||
import { TransformStage } from './transform-stage';
|
import { TransformStage } from './transform-stage';
|
||||||
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础节点
|
* 基础节点
|
||||||
@ -122,7 +124,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get title(): TitleContent {
|
@computed get title(): string | I18nData | ReactElement {
|
||||||
let t = this.getExtraProp('title');
|
let t = this.getExtraProp('title');
|
||||||
if (!t && this.componentMeta.descriptor) {
|
if (!t && this.componentMeta.descriptor) {
|
||||||
t = this.getProp(this.componentMeta.descriptor, false);
|
t = this.getProp(this.componentMeta.descriptor, false);
|
||||||
@ -136,6 +138,10 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
|||||||
return this.componentMeta.title;
|
return this.componentMeta.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get icon() {
|
||||||
|
return this.componentMeta.icon;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(readonly document: DocumentModel, nodeSchema: Schema) {
|
constructor(readonly document: DocumentModel, nodeSchema: Schema) {
|
||||||
const { componentName, id, children, props, ...extras } = nodeSchema;
|
const { componentName, id, children, props, ...extras } = nodeSchema;
|
||||||
this.id = id || `node$${document.nextId()}`;
|
this.id = id || `node$${document.nextId()}`;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import './renderer.less';
|
|||||||
|
|
||||||
// patch cloneElement avoid lost keyProps
|
// patch cloneElement avoid lost keyProps
|
||||||
const originCloneElement = window.React.cloneElement;
|
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) {
|
if (child.ref && props.ref) {
|
||||||
const dRef = props.ref;
|
const dRef = props.ref;
|
||||||
const cRef = child.ref;
|
const cRef = child.ref;
|
||||||
@ -18,7 +18,7 @@ const originCloneElement = window.React.cloneElement;
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
cRef.current = x;
|
cRef.current = x;
|
||||||
} catch (e) { }
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dRef) {
|
if (dRef) {
|
||||||
@ -27,13 +27,13 @@ const originCloneElement = window.React.cloneElement;
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
dRef.current = x;
|
dRef.current = x;
|
||||||
} catch (e) { }
|
} catch (e) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
return originCloneElement(child, props);
|
return originCloneElement(child, props);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default class SimulatorRendererView extends Component<{ renderer: SimulatorRenderer }> {
|
export default class SimulatorRendererView extends Component<{ renderer: SimulatorRenderer }> {
|
||||||
render() {
|
render() {
|
||||||
@ -97,7 +97,7 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
|
|||||||
return createElement(
|
return createElement(
|
||||||
Component,
|
Component,
|
||||||
viewProps,
|
viewProps,
|
||||||
children == null ? null : Array.isArray(children) ? children : [children],
|
children == null ? [] : Array.isArray(children) ? children : [children],
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onCompGetRef={(schema: any, ref: ReactInstance | null) => {
|
onCompGetRef={(schema: any, ref: ReactInstance | null) => {
|
||||||
|
|||||||
@ -13,3 +13,6 @@ export interface TitleConfig {
|
|||||||
|
|
||||||
export type TitleContent = string | I18nData | ReactElement | TitleConfig;
|
export type TitleContent = string | I18nData | ReactElement | TitleConfig;
|
||||||
|
|
||||||
|
export function isTitleConfig(obj: any): obj is TitleConfig {
|
||||||
|
return obj && (obj.label || obj.tip || obj.icon);
|
||||||
|
}
|
||||||
|
|||||||
@ -100,6 +100,22 @@ export default class Bundle {
|
|||||||
cp.setView(view);
|
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 {
|
private recursivelyRegisterViews(list: any[], viewName?: string): void {
|
||||||
list.forEach((item: any) => {
|
list.forEach((item: any) => {
|
||||||
if (Array.isArray(item.module)) {
|
if (Array.isArray(item.module)) {
|
||||||
|
|||||||
82
packages/vision-preset/src/components/index.less
Normal file
82
packages/vision-preset/src/components/index.less
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
@import '~@ali/ve-less-variables/index.less';
|
||||||
|
|
||||||
|
// 样式直接沿用之前的样式,优化了下命名
|
||||||
|
.instance-node-selector {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 2px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
border-radius: @global-border-radius;
|
||||||
|
margin-right: 2px;
|
||||||
|
pointer-events: auto;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 5px;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
max-width: inherit;
|
||||||
|
path {
|
||||||
|
fill: var(--color-icon-white, @title-bgcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-current {
|
||||||
|
background: var(--color-brand, @brand-color-1);
|
||||||
|
padding: 0 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
padding-right: 6px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-list {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
&-node {
|
||||||
|
margin: 2px 0;
|
||||||
|
&-content {
|
||||||
|
padding-left: 6px;
|
||||||
|
background: #78869a;
|
||||||
|
display: inline-flex;
|
||||||
|
border-radius: 3px;
|
||||||
|
align-items: center;
|
||||||
|
height: 20px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
&-title {
|
||||||
|
padding-right: 6px;
|
||||||
|
// margin-left: 5px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.instance-node-selector-current {
|
||||||
|
color: ar(--color-text-reverse, @white-alpha-2);
|
||||||
|
}
|
||||||
|
.instance-node-selector-popup {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
transition: 0.2s all ease-in;
|
||||||
|
}
|
||||||
|
}
|
||||||
95
packages/vision-preset/src/components/index.tsx
Normal file
95
packages/vision-preset/src/components/index.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { Overlay } from '@alifd/next';
|
||||||
|
import React from 'react';
|
||||||
|
import './index.less';
|
||||||
|
import { Title } from '@ali/lowcode-editor-core';
|
||||||
|
|
||||||
|
import { Node, ParentalNode } from '@ali/lowcode-designer';
|
||||||
|
|
||||||
|
const { Popup } = Overlay;
|
||||||
|
|
||||||
|
export interface IProps {
|
||||||
|
node: Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IState {
|
||||||
|
parentNodes: Node[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnionNode = Node | ParentalNode | null;
|
||||||
|
|
||||||
|
export class InstanceNodeSelector extends React.Component<IProps, IState> {
|
||||||
|
state: IState = {
|
||||||
|
parentNodes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const parentNodes = this.getParentNodes(this.props.node);
|
||||||
|
this.setState({
|
||||||
|
parentNodes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取节点的父级节点(最多获取5层)
|
||||||
|
getParentNodes = (node: Node) => {
|
||||||
|
const parentNodes = [];
|
||||||
|
let currentNode: UnionNode = node;
|
||||||
|
|
||||||
|
while (currentNode && parentNodes.length < 5) {
|
||||||
|
currentNode = currentNode.getParent();
|
||||||
|
if (currentNode) {
|
||||||
|
parentNodes.push(currentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parentNodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
onSelect = (node: Node) => () => {
|
||||||
|
if (node && typeof node.select === 'function') {
|
||||||
|
node.select();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderNodes = (node: Node) => {
|
||||||
|
const nodes = this.state.parentNodes || [];
|
||||||
|
const children = nodes.map((node, key) => {
|
||||||
|
return (
|
||||||
|
<div key={key} onClick={this.onSelect(node)} className="instance-node-selector-node">
|
||||||
|
<div className="instance-node-selector-node-content">
|
||||||
|
<Title
|
||||||
|
className="instance-node-selector-node-title"
|
||||||
|
title={{
|
||||||
|
label: node.title,
|
||||||
|
icon: node.icon,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { node } = this.props;
|
||||||
|
return (
|
||||||
|
<div className="instance-node-selector">
|
||||||
|
<Popup
|
||||||
|
trigger={
|
||||||
|
<div className="instance-node-selector-current">
|
||||||
|
<Title
|
||||||
|
className="instance-node-selector-node-title"
|
||||||
|
title={{
|
||||||
|
label: node.title,
|
||||||
|
icon: node.icon,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
triggerType="hover"
|
||||||
|
>
|
||||||
|
<div className="instance-node-selector">{this.renderNodes(node)}</div>
|
||||||
|
</Popup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,18 @@
|
|||||||
import { isJSBlock, isJSSlot } from '@ali/lowcode-types';
|
import { isJSBlock, isJSSlot } from '@ali/lowcode-types';
|
||||||
import { isPlainObject } from '@ali/lowcode-utils';
|
import { isPlainObject } from '@ali/lowcode-utils';
|
||||||
import { globalContext, Editor, registerSetter } from '@ali/lowcode-editor-core';
|
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||||
import { Designer, TransformStage } from '@ali/lowcode-designer';
|
import { Designer, TransformStage, addBuiltinComponentAction } from '@ali/lowcode-designer';
|
||||||
// import { registerSetters } from '@ali/lowcode-setters';
|
import { registerSetters } from '@ali/lowcode-setters';
|
||||||
import Outline from '@ali/lowcode-plugin-outline-pane';
|
// import Outline from '@ali/lowcode-plugin-outline-pane';
|
||||||
|
|
||||||
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
||||||
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
|
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
|
||||||
|
|
||||||
import Preview from '@ali/lowcode-plugin-sample-preview';
|
import Preview from '@ali/lowcode-plugin-sample-preview';
|
||||||
// import SourceEditor from '@ali/lowcode-plugin-source-editor';
|
// import SourceEditor from '@ali/lowcode-plugin-source-editor';
|
||||||
import { i18nReducer } from './i18n-reducer';
|
import { i18nReducer } from './i18n-reducer';
|
||||||
|
import { InstanceNodeSelector } from './components';
|
||||||
|
import { Divider } from '@alifd/next';
|
||||||
|
|
||||||
export const editor = new Editor();
|
export const editor = new Editor();
|
||||||
globalContext.register(editor, Editor);
|
globalContext.register(editor, Editor);
|
||||||
@ -78,15 +81,15 @@ skeleton.add({
|
|||||||
type: 'Panel',
|
type: 'Panel',
|
||||||
content: SettingsPrimaryPane,
|
content: SettingsPrimaryPane,
|
||||||
});
|
});
|
||||||
skeleton.add({
|
// skeleton.add({
|
||||||
area: 'leftArea',
|
// area: 'leftArea',
|
||||||
name: 'outlinePane',
|
// name: 'outlinePane',
|
||||||
type: 'PanelDock',
|
// type: 'PanelDock',
|
||||||
content: Outline,
|
// content: Outline,
|
||||||
panelProps: {
|
// panelProps: {
|
||||||
area: 'leftFixedArea',
|
// area: 'leftFixedArea',
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
skeleton.add({
|
skeleton.add({
|
||||||
area: 'topArea',
|
area: 'topArea',
|
||||||
@ -112,3 +115,10 @@ skeleton.add({
|
|||||||
// },
|
// },
|
||||||
// content: SourceEditor,
|
// content: SourceEditor,
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
// 实例节点选择器,线框高亮
|
||||||
|
addBuiltinComponentAction({
|
||||||
|
name: 'instance-node-selector',
|
||||||
|
content: InstanceNodeSelector,
|
||||||
|
important: true,
|
||||||
|
});
|
||||||
|
|||||||
@ -12,4 +12,13 @@ export default {
|
|||||||
const nodes = designer.currentSelection?.getNodes();
|
const nodes = designer.currentSelection?.getNodes();
|
||||||
return nodes?.[0];
|
return nodes?.[0];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* TODO dirty fix
|
||||||
|
*/
|
||||||
|
onIntoView(func: (node: any, insertion: any) => any) {
|
||||||
|
// this.emitter.on('intoview', func);
|
||||||
|
return () => {
|
||||||
|
// this.emitter.removeListener('intoview', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import * as utils from '@ali/ve-utils';
|
import * as utils from '@ali/ve-utils';
|
||||||
import Popup from '@ali/ve-popups';
|
import Popup from '@ali/ve-popups';
|
||||||
import Icons from '@ali/ve-icons';
|
import Icons from '@ali/ve-icons';
|
||||||
|
import logger from '@ali/vu-logger';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import I18nUtil from '@ali/ve-i18n-util';
|
import I18nUtil from '@ali/ve-i18n-util';
|
||||||
import { hotkey as Hotkey } from '@ali/lowcode-editor-core';
|
import { hotkey as Hotkey } from '@ali/lowcode-editor-core';
|
||||||
import { createElement } from 'react';
|
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 Bus from './bus';
|
||||||
import { skeleton } from './editor';
|
import { skeleton } from './editor';
|
||||||
import { Workbench } from '@ali/lowcode-editor-skeleton';
|
import { Workbench } from '@ali/lowcode-editor-skeleton';
|
||||||
@ -21,6 +22,8 @@ import * as Field from './fields';
|
|||||||
import Prop from './prop';
|
import Prop from './prop';
|
||||||
import Env from './env';
|
import Env from './env';
|
||||||
import DragEngine from './drag-engine';
|
import DragEngine from './drag-engine';
|
||||||
|
import Viewport from './viewport';
|
||||||
|
import Project from './project';
|
||||||
import { designer, editor } from './editor';
|
import { designer, editor } from './editor';
|
||||||
|
|
||||||
import './vision.less';
|
import './vision.less';
|
||||||
@ -101,6 +104,10 @@ const VisualEngine = {
|
|||||||
Bundle,
|
Bundle,
|
||||||
Pages,
|
Pages,
|
||||||
DragEngine,
|
DragEngine,
|
||||||
|
Viewport,
|
||||||
|
Version,
|
||||||
|
Project,
|
||||||
|
logger,
|
||||||
};
|
};
|
||||||
|
|
||||||
(window as any).VisualEngine = VisualEngine;
|
(window as any).VisualEngine = VisualEngine;
|
||||||
@ -144,6 +151,10 @@ export {
|
|||||||
Bundle,
|
Bundle,
|
||||||
Pages,
|
Pages,
|
||||||
DragEngine,
|
DragEngine,
|
||||||
|
Viewport,
|
||||||
|
Version,
|
||||||
|
Project,
|
||||||
|
logger,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,8 +15,8 @@ const pages = Object.assign(project, {
|
|||||||
project.load({
|
project.load({
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
componentsMap: [],
|
componentsMap: [],
|
||||||
componentsTree: pages.map(page => page.layout),
|
componentsTree: pages[0].componentsTree,
|
||||||
});
|
}, true);
|
||||||
},
|
},
|
||||||
addPage(data: OldPageData) {
|
addPage(data: OldPageData) {
|
||||||
return project.open(data.layout);
|
return project.open(data.layout);
|
||||||
|
|||||||
17
packages/vision-preset/src/project.ts
Normal file
17
packages/vision-preset/src/project.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
class Project {
|
||||||
|
private schema: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.schema = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchema() {
|
||||||
|
return this.schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSchema(schema: any) {
|
||||||
|
this.schema = schema;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Project();
|
||||||
278
packages/vision-preset/src/viewport.ts
Normal file
278
packages/vision-preset/src/viewport.ts
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
const domReady = require('domready');
|
||||||
|
import Flags from './flags';
|
||||||
|
|
||||||
|
function enterFullscreen() {
|
||||||
|
const elem = document.documentElement;
|
||||||
|
if (elem.requestFullscreen) {
|
||||||
|
elem.requestFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitFullscreen() {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFullscreen() {
|
||||||
|
return document.fullscreen || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStyleResourceConfig {
|
||||||
|
media?: string;
|
||||||
|
type?: string;
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class StyleResource {
|
||||||
|
config: IStyleResourceConfig;
|
||||||
|
styleElement: HTMLStyleElement;
|
||||||
|
mounted: boolean;
|
||||||
|
inited: boolean;
|
||||||
|
|
||||||
|
constructor(config: IStyleResourceConfig) {
|
||||||
|
this.config = config || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
matchDevice(device: string) {
|
||||||
|
const media = this.config.media;
|
||||||
|
|
||||||
|
if (!media || media === 'ALL' || media === '*') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return media.toUpperCase() === device.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if (this.inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inited = true;
|
||||||
|
|
||||||
|
const { type, content } = this.config;
|
||||||
|
|
||||||
|
let styleElement;
|
||||||
|
if (type === 'URL') {
|
||||||
|
styleElement = document.createElement('link');
|
||||||
|
styleElement.href = content || '';
|
||||||
|
styleElement.rel = 'stylesheet';
|
||||||
|
} else {
|
||||||
|
styleElement = document.createElement('style');
|
||||||
|
styleElement.setAttribute('type', 'text/css');
|
||||||
|
if (styleElement.styleSheet) {
|
||||||
|
styleElement.styleSheet.cssText = content;
|
||||||
|
} else {
|
||||||
|
styleElement.appendChild(document.createTextNode(content || ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.styleElement = styleElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply() {
|
||||||
|
if (this.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
document.head.appendChild(this.styleElement);
|
||||||
|
this.mounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unmount() {
|
||||||
|
if (!this.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.head.removeChild(this.styleElement);
|
||||||
|
this.mounted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Viewport {
|
||||||
|
preview: boolean;
|
||||||
|
focused: boolean;
|
||||||
|
slateFixed: boolean;
|
||||||
|
emitter: EventEmitter;
|
||||||
|
device: string;
|
||||||
|
focusTarget: any;
|
||||||
|
cssResourceSet: StyleResource[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.preview = false;
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
document.addEventListener('webkitfullscreenchange', () => {
|
||||||
|
this.emitter.emit('fullscreenchange', this.isFullscreen());
|
||||||
|
});
|
||||||
|
domReady(() => this.applyMediaCSS());
|
||||||
|
}
|
||||||
|
|
||||||
|
setFullscreen(flag: boolean) {
|
||||||
|
const fullscreen = this.isFullscreen();
|
||||||
|
if (fullscreen && !flag) {
|
||||||
|
exitFullscreen();
|
||||||
|
} else if (!fullscreen && flag) {
|
||||||
|
enterFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFullscreen() {
|
||||||
|
if (this.isFullscreen()) {
|
||||||
|
exitFullscreen();
|
||||||
|
} else {
|
||||||
|
enterFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFullscreen() {
|
||||||
|
return isFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocus(flag: boolean) {
|
||||||
|
if (this.focused && !flag) {
|
||||||
|
this.focused = false;
|
||||||
|
Flags.remove('view-focused');
|
||||||
|
this.emitter.emit('focuschange', false);
|
||||||
|
} else if (!this.focused && flag) {
|
||||||
|
this.focused = true;
|
||||||
|
Flags.add('view-focused');
|
||||||
|
this.emitter.emit('focuschange', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocusTarget(focusTarget: any) {
|
||||||
|
this.focusTarget = focusTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
returnFocus() {
|
||||||
|
if (this.focusTarget) {
|
||||||
|
this.focusTarget.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFocus() {
|
||||||
|
return this.focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPreview(flag: boolean) {
|
||||||
|
if (this.preview && !flag) {
|
||||||
|
this.preview = false;
|
||||||
|
Flags.setPreviewMode(false);
|
||||||
|
this.emitter.emit('preview', false);
|
||||||
|
this.changeViewport();
|
||||||
|
} else if (!this.preview && flag) {
|
||||||
|
this.preview = true;
|
||||||
|
Flags.setPreviewMode(true);
|
||||||
|
this.emitter.emit('preview', true);
|
||||||
|
this.changeViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePreview() {
|
||||||
|
if (this.isPreview()) {
|
||||||
|
this.setPreview(false);
|
||||||
|
} else {
|
||||||
|
this.setPreview(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreview() {
|
||||||
|
return this.preview;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDevice(device = 'pc') {
|
||||||
|
if (this.getDevice() !== device) {
|
||||||
|
this.device = device;
|
||||||
|
Flags.setSimulator(device);
|
||||||
|
this.applyMediaCSS();
|
||||||
|
this.emitter.emit('devicechange', device);
|
||||||
|
this.changeViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDevice() {
|
||||||
|
return this.device || 'pc';
|
||||||
|
}
|
||||||
|
|
||||||
|
changeViewport() {
|
||||||
|
this.emitter.emit('viewportchange', this.getViewport());
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewport() {
|
||||||
|
return `${this.isPreview() ? 'preview' : 'design'}-${this.getDevice()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMediaCSS() {
|
||||||
|
if (!document.head || !this.cssResourceSet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const device = this.getDevice();
|
||||||
|
this.cssResourceSet.forEach((item) => {
|
||||||
|
if (item.matchDevice(device)) {
|
||||||
|
item.apply();
|
||||||
|
} else {
|
||||||
|
item.unmount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setGlobalCSS(resourceSet: IStyleResourceConfig[]) {
|
||||||
|
if (this.cssResourceSet) {
|
||||||
|
this.cssResourceSet.forEach((item) => {
|
||||||
|
item.unmount();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.cssResourceSet = resourceSet.map((item: IStyleResourceConfig) => new StyleResource(item)).reverse();
|
||||||
|
this.applyMediaCSS();
|
||||||
|
}
|
||||||
|
|
||||||
|
setWithShell(shell: string) {
|
||||||
|
Flags.setWithShell(shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFullscreenChange(func: () => any) {
|
||||||
|
this.emitter.on('fullscreenchange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('fullscreenchange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreview(func: () => any) {
|
||||||
|
this.emitter.on('preview', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('preview', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeviceChange(func: () => any) {
|
||||||
|
this.emitter.on('devicechange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('devicechange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onSlateFixedChange(func: (flag: boolean) => any) {
|
||||||
|
this.emitter.on('slatefixed', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('slatefixed', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onViewportChange(func: () => any) {
|
||||||
|
this.emitter.on('viewportchange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('viewportchange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocusChange(func: (flag: boolean) => any) {
|
||||||
|
this.emitter.on('focuschange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('focuschange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Viewport();
|
||||||
Loading…
x
Reference in New Issue
Block a user