mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-16 23:02:50 +00:00
complete outline
This commit is contained in:
parent
5f0ac69595
commit
b849377de4
@ -1,20 +0,0 @@
|
|||||||
.my-auxiliary {
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
overflow: visible;
|
|
||||||
z-index: 800;
|
|
||||||
.embed-editor-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
> * {
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
import { observer } from '@ali/recore';
|
|
||||||
import { Component } from 'react';
|
|
||||||
import { getCurrentDocument } from '../../globals';
|
|
||||||
import './auxiliary.less';
|
|
||||||
import { EdgingView } from './gliding';
|
|
||||||
import { InsertionView } from './insertion';
|
|
||||||
import { SelectingView } from './selecting';
|
|
||||||
import EmbedEditorToolbar from './embed-editor-toolbar';
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class AuxiliaryView extends Component {
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const doc = getCurrentDocument();
|
|
||||||
if (!doc || !doc.ready) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const { scrollX, scrollY, scale } = doc.viewport;
|
|
||||||
return (
|
|
||||||
<div className="my-auxiliary" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
|
|
||||||
<EmbedEditorToolbar />
|
|
||||||
<EdgingView />
|
|
||||||
<InsertionView />
|
|
||||||
<SelectingView />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
.my-edging {
|
|
||||||
box-sizing: border-box;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
border: 1px dashed var(--color-brand-light);
|
|
||||||
z-index: 1;
|
|
||||||
background: rgba(95, 240, 114, 0.04);
|
|
||||||
will-change: transform, width, height;
|
|
||||||
transition-property: transform, width, height;
|
|
||||||
transition-duration: 60ms;
|
|
||||||
transition-timing-function: linear;
|
|
||||||
overflow: visible;
|
|
||||||
>.title {
|
|
||||||
position: absolute;
|
|
||||||
color: var(--color-brand-light);
|
|
||||||
top: -20px;
|
|
||||||
left: 0;
|
|
||||||
font-weight: lighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.x-shadow {
|
|
||||||
border-color: rgba(138, 93, 226, 0.8);
|
|
||||||
background: rgba(138, 93, 226, 0.04);
|
|
||||||
|
|
||||||
>.title {
|
|
||||||
color: rgba(138, 93, 226, 1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.x-flow {
|
|
||||||
border-color: rgba(255, 99, 8, 0.8);
|
|
||||||
background: rgba(255, 99, 8, 0.04);
|
|
||||||
>.title {
|
|
||||||
color: rgb(255, 99, 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
import { observer } from '@recore/core-obx';
|
|
||||||
import { Component } from 'react';
|
|
||||||
import './edging.less';
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class GlidingView extends Component {
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const node = edging.watching;
|
|
||||||
if (!node || !edging.enable || (current.selection && current.selection.has(node.id))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: think of multi ReactInstance
|
|
||||||
// TODO: findDOMNode cause a render bug
|
|
||||||
const rect = node.document.computeRect(node);
|
|
||||||
if (!rect) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { scale, scrollTarget } = node.document.viewport;
|
|
||||||
|
|
||||||
const sx = scrollTarget!.left;
|
|
||||||
const sy = scrollTarget!.top;
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
width: rect.width * scale,
|
|
||||||
height: rect.height * scale,
|
|
||||||
transform: `translate(${(sx + rect.left) * scale}px, ${(sy + rect.top) * scale}px)`,
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
let className = 'my-edging';
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// 1. thinkof icon
|
|
||||||
// 2. thinkof top|bottom|inner space
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className} style={style}>
|
|
||||||
<a className="title">{(node as any).title || node.tagName}</a>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
import { obx } from '@ali/recore';
|
|
||||||
import { INode } from '../../document/node';
|
|
||||||
|
|
||||||
export default class OffsetObserver {
|
|
||||||
@obx.ref offsetTop = 0;
|
|
||||||
@obx.ref offsetLeft = 0;
|
|
||||||
@obx.ref offsetRight = 0;
|
|
||||||
@obx.ref offsetBottom = 0;
|
|
||||||
@obx.ref height = 0;
|
|
||||||
@obx.ref width = 0;
|
|
||||||
@obx.ref hasOffset = false;
|
|
||||||
@obx.ref left = 0;
|
|
||||||
@obx.ref top = 0;
|
|
||||||
@obx.ref right = 0;
|
|
||||||
@obx.ref bottom = 0;
|
|
||||||
|
|
||||||
private pid: number | undefined;
|
|
||||||
|
|
||||||
constructor(node: INode) {
|
|
||||||
const document = node.document;
|
|
||||||
const scrollTarget = document.viewport.scrollTarget!;
|
|
||||||
|
|
||||||
let pid: number;
|
|
||||||
const compute = () => {
|
|
||||||
if (pid !== this.pid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rect = document.computeRect(node);
|
|
||||||
if (!rect) {
|
|
||||||
this.hasOffset = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.hasOffset = true;
|
|
||||||
this.offsetLeft = rect.left + scrollTarget.left;
|
|
||||||
this.offsetRight = rect.right + scrollTarget.left;
|
|
||||||
this.offsetTop = rect.top + scrollTarget.top;
|
|
||||||
this.offsetBottom = rect.bottom + scrollTarget.top;
|
|
||||||
this.height = rect.height;
|
|
||||||
this.width = rect.width;
|
|
||||||
this.left = rect.left;
|
|
||||||
this.top = rect.top;
|
|
||||||
this.right = rect.right;
|
|
||||||
this.bottom = rect.bottom;
|
|
||||||
this.pid = pid = (window as any).requestIdleCallback(compute);
|
|
||||||
};
|
|
||||||
|
|
||||||
// try first
|
|
||||||
compute();
|
|
||||||
// try second, ensure the dom mounted
|
|
||||||
this.pid = pid = (window as any).requestIdleCallback(compute);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
if (this.pid) {
|
|
||||||
(window as any).cancelIdleCallback(this.pid);
|
|
||||||
}
|
|
||||||
this.pid = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
.my-selecting {
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
border: 1px solid var(--color-brand-light);
|
|
||||||
z-index: 2;
|
|
||||||
overflow: visible;
|
|
||||||
>.title {
|
|
||||||
position: absolute;
|
|
||||||
color: var(--color-brand-light);
|
|
||||||
top: -20px;
|
|
||||||
left: 0;
|
|
||||||
font-weight: lighter;
|
|
||||||
}
|
|
||||||
&.dragging {
|
|
||||||
background: rgba(182, 178, 178, 0.8);
|
|
||||||
border: none;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.x-shadow {
|
|
||||||
border-color: rgba(147, 112, 219, 1.0);
|
|
||||||
background: rgba(147, 112, 219, 0.04);
|
|
||||||
>.title {
|
|
||||||
color: rgba(147, 112, 219, 1.0);
|
|
||||||
}
|
|
||||||
&.highlight {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.x-flow {
|
|
||||||
border-color: rgb(255, 99, 8);
|
|
||||||
>.title {
|
|
||||||
color: rgb(255, 99, 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
import { observer } from '@ali/recore';
|
|
||||||
import { Component, Fragment } from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { INode, isElementNode, isConfettiNode, hasConditionFlow } from '../../document/node';
|
|
||||||
import OffsetObserver from './offset-observer';
|
|
||||||
import './selecting.less';
|
|
||||||
import { isShadowNode, isShadowsContainer } from '../../document/node/shadow-node';
|
|
||||||
import { isConditionFlow } from '../../document/node/condition-flow';
|
|
||||||
import { current, dragon } from '../../globals';
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class SingleSelectingView extends Component<{ node: INode; highlight?: boolean }> {
|
|
||||||
private offsetObserver: OffsetObserver;
|
|
||||||
|
|
||||||
constructor(props: { node: INode; highlight?: boolean }) {
|
|
||||||
super(props);
|
|
||||||
this.offsetObserver = new OffsetObserver(props.node);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (!this.offsetObserver.hasOffset) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scale = this.props.node.document.viewport.scale;
|
|
||||||
const { width, height, offsetTop, offsetLeft } = this.offsetObserver;
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
width: width * scale,
|
|
||||||
height: height * scale,
|
|
||||||
transform: `translate3d(${offsetLeft * scale}px, ${offsetTop * scale}px, 0)`,
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
const { node, highlight } = this.props;
|
|
||||||
|
|
||||||
const className = classNames('my-selecting', {
|
|
||||||
'x-shadow': isShadowNode(node),
|
|
||||||
'x-flow': hasConditionFlow(node) || isConditionFlow(node),
|
|
||||||
highlight,
|
|
||||||
});
|
|
||||||
|
|
||||||
return <div className={className} style={style} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class SelectingView extends Component {
|
|
||||||
get selecting(): INode[] {
|
|
||||||
const sel = current.selection;
|
|
||||||
if (!sel) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
if (dragon.dragging) {
|
|
||||||
return sel.getTopNodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
return sel.getNodes();
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
return this.selecting.map(node => {
|
|
||||||
// select all nodes when doing x-for
|
|
||||||
if (isShadowsContainer(node)) {
|
|
||||||
// FIXME: thinkof nesting for
|
|
||||||
const views = [];
|
|
||||||
for (const shadowNode of (node as any).getShadows()!.values()) {
|
|
||||||
views.push(<SingleSelectingView key={shadowNode.id} node={shadowNode} />);
|
|
||||||
}
|
|
||||||
return <Fragment key={node.id}>{views}</Fragment>;
|
|
||||||
} else if (isShadowNode(node)) {
|
|
||||||
const shadows = node.origin.getShadows()!.values();
|
|
||||||
const views = [];
|
|
||||||
for (const shadowNode of shadows) {
|
|
||||||
views.push(<SingleSelectingView highlight={shadowNode === node} key={shadowNode.id} node={shadowNode} />);
|
|
||||||
}
|
|
||||||
return <Fragment key={node.id}>{views}</Fragment>;
|
|
||||||
}
|
|
||||||
// select the visible node when doing x-if
|
|
||||||
else if (isConditionFlow(node)) {
|
|
||||||
return <SingleSelectingView node={node.visibleNode} key={node.id} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <SingleSelectingView node={node} key={node.id} />;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
.lc-auxiliary {
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: visible;
|
||||||
|
z-index: 800;
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { observer } from '@recore/core-obx';
|
||||||
|
import { Component } from 'react';
|
||||||
|
import { OutlineHovering } from './outline-hovering';
|
||||||
|
import { SimulatorContext } from '../context';
|
||||||
|
import { SimulatorHost } from '../host';
|
||||||
|
import './auxiliary.less';
|
||||||
|
import './outlines.less';
|
||||||
|
import { OutlineSelecting } from './outline-selecting';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class AuxiliaryView extends Component {
|
||||||
|
static contextType = SimulatorContext;
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const host = this.context as SimulatorHost;
|
||||||
|
const { scrollX, scrollY, scale } = host.viewport;
|
||||||
|
return (
|
||||||
|
<div className="lc-auxiliary" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
|
||||||
|
<OutlineHovering key="hovering" />
|
||||||
|
<OutlineSelecting key="selecting" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
import { Component, Fragment, PureComponent } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { observer } from '@recore/core-obx';
|
||||||
|
import { SimulatorContext } from '../context';
|
||||||
|
import { SimulatorHost } from '../host';
|
||||||
|
import { computed } from '@recore/obx';
|
||||||
|
|
||||||
|
export class OutlineHoveringInstance extends PureComponent<{
|
||||||
|
title: string;
|
||||||
|
rect: DOMRect | null;
|
||||||
|
scale: number;
|
||||||
|
scrollX: number;
|
||||||
|
scrollY: number;
|
||||||
|
}> {
|
||||||
|
render() {
|
||||||
|
const { title, rect, scale, scrollX, scrollY } = this.props;
|
||||||
|
if (!rect) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
width: rect.width * scale,
|
||||||
|
height: rect.height * scale,
|
||||||
|
transform: `translate(${(scrollX + rect.left) * scale}px, ${(scrollY + rect.top) * scale}px)`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const className = classNames('lc-outlines lc-outlines-hovering');
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// 1. thinkof icon
|
||||||
|
// 2. thinkof top|bottom|inner space
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} style={style}>
|
||||||
|
<a className="lc-outlines-title">{title}</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class OutlineHovering extends Component {
|
||||||
|
static contextType = SimulatorContext;
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get scale() {
|
||||||
|
return (this.context as SimulatorHost).viewport.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get scrollX() {
|
||||||
|
return (this.context as SimulatorHost).viewport.scrollX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get scrollY() {
|
||||||
|
return (this.context as SimulatorHost).viewport.scrollY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get current() {
|
||||||
|
const host = this.context as SimulatorHost;
|
||||||
|
const doc = host.document;
|
||||||
|
const selection = doc.selection;
|
||||||
|
const current = host.designer.hovering.current;
|
||||||
|
if (!current || current.document !== doc || selection.has(current.id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const host = this.context as SimulatorHost;
|
||||||
|
const current = this.current;
|
||||||
|
if (!current) {
|
||||||
|
return <Fragment />;
|
||||||
|
}
|
||||||
|
const instances = host.getComponentInstance(current);
|
||||||
|
if (!instances || instances.length < 1) {
|
||||||
|
return <Fragment />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instances.length === 1) {
|
||||||
|
return (
|
||||||
|
<OutlineHoveringInstance
|
||||||
|
key="line-s"
|
||||||
|
title={current.title}
|
||||||
|
scale={this.scale}
|
||||||
|
scrollX={this.scrollX}
|
||||||
|
scrollY={this.scrollY}
|
||||||
|
rect={host.computeComponentInstanceRect(instances[0])}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{instances.map((inst, i) => (
|
||||||
|
<OutlineHoveringInstance
|
||||||
|
key={`line-${i}`}
|
||||||
|
title={current.title}
|
||||||
|
scale={this.scale}
|
||||||
|
scrollX={this.scrollX}
|
||||||
|
scrollY={this.scrollY}
|
||||||
|
rect={host.computeComponentInstanceRect(inst)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
import { Component, Fragment } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { observer } from '@recore/core-obx';
|
||||||
|
import { SimulatorContext } from '../context';
|
||||||
|
import { SimulatorHost } from '../host';
|
||||||
|
import { computed } from '@recore/obx';
|
||||||
|
import OffsetObserver from '../../../../designer/offset-observer';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class OutlineSelectingInstance extends Component<{ observed: OffsetObserver; highlight?: boolean }> {
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { observed, highlight } = this.props;
|
||||||
|
if (!observed.hasOffset) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { scale, width, height, offsetTop, offsetLeft } = observed;
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
width: width * scale,
|
||||||
|
height: height * scale,
|
||||||
|
transform: `translate3d(${offsetLeft * scale}px, ${offsetTop * scale}px, 0)`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const className = classNames('lc-outlines lc-outlines-selecting', {
|
||||||
|
highlight,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} style={style}>
|
||||||
|
<a className="lc-outlines-title">{observed.nodeInstance.node.title}</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class OutlineSelecting extends Component {
|
||||||
|
static contextType = SimulatorContext;
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get selecting() {
|
||||||
|
const doc = this.host.document;
|
||||||
|
if (doc.suspensed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return doc.selection.getNodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get host(): SimulatorHost {
|
||||||
|
return this.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const selecting = this.selecting;
|
||||||
|
if (!selecting || selecting.length < 1) {
|
||||||
|
// DIRTY FIX, recore has a bug!
|
||||||
|
return <Fragment />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const designer = this.host.designer;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{selecting.map(node => {
|
||||||
|
const instances = this.host.getComponentInstance(node);
|
||||||
|
if (!instances || instances.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Fragment key={node.id}>
|
||||||
|
{instances.map((instance, i) => {
|
||||||
|
const observed = designer.createOffsetObserver({
|
||||||
|
node,
|
||||||
|
instance,
|
||||||
|
});
|
||||||
|
if (!observed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <OutlineSelectingInstance key={`line-s-${i}`} observed={observed} />;
|
||||||
|
})}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
@scope: lc-outlines;
|
||||||
|
|
||||||
|
.@{scope} {
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
border: 1px solid var(--color-brand-light);
|
||||||
|
will-change: transform, width, height;
|
||||||
|
overflow: visible;
|
||||||
|
& > &-title {
|
||||||
|
position: absolute;
|
||||||
|
color: var(--color-brand-light);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateY(-100%);
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
&&-hovering {
|
||||||
|
z-index: 1;
|
||||||
|
border-style: dashed;
|
||||||
|
background: rgba(95, 240, 114, 0.04);
|
||||||
|
|
||||||
|
&.x-loop {
|
||||||
|
border-color: rgba(138, 93, 226, 0.8);
|
||||||
|
background: rgba(138, 93, 226, 0.04);
|
||||||
|
|
||||||
|
>.@{scope}-title {
|
||||||
|
color: rgba(138, 93, 226, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.x-condition {
|
||||||
|
border-color: rgba(255, 99, 8, 0.8);
|
||||||
|
background: rgba(255, 99, 8, 0.04);
|
||||||
|
>.@{scope}-title {
|
||||||
|
color: rgb(255, 99, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&&-selecting {
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&.x-loop {
|
||||||
|
border-color: rgba(147, 112, 219, 1.0);
|
||||||
|
background: rgba(147, 112, 219, 0.04);
|
||||||
|
|
||||||
|
>@{scope}-title {
|
||||||
|
color: rgba(147, 112, 219, 1.0);
|
||||||
|
}
|
||||||
|
&.highlight {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.x-condition {
|
||||||
|
border-color: rgb(255, 99, 8);
|
||||||
|
>@{scope}-title {
|
||||||
|
color: rgb(255, 99, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
background: rgba(182, 178, 178, 0.8);
|
||||||
|
border: none;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages/designer/src/builtins/simulator/host/context.ts
Normal file
4
packages/designer/src/builtins/simulator/host/context.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import { SimulatorHost } from './host';
|
||||||
|
|
||||||
|
export const SimulatorContext = createContext<SimulatorHost>({} as any);
|
||||||
@ -1,8 +1,9 @@
|
|||||||
import { Component, createContext } from 'react';
|
import { Component } from 'react';
|
||||||
import { observer } from '@recore/core-obx';
|
import { observer } from '@recore/core-obx';
|
||||||
// import { AuxiliaryView } from '../auxilary';
|
|
||||||
import { SimulatorHost, SimulatorProps } from './host';
|
import { SimulatorHost, SimulatorProps } from './host';
|
||||||
import DocumentModel from '../../../designer/document/document-model';
|
import DocumentModel from '../../../designer/document/document-model';
|
||||||
|
import { SimulatorContext } from './context';
|
||||||
|
import { AuxiliaryView } from './auxilary';
|
||||||
import './host.less';
|
import './host.less';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -14,8 +15,6 @@ import './host.less';
|
|||||||
Auxiliary 辅助显示层,初始相对 Content 位置 0,0,紧贴 Canvas, 根据 Content 滚动位置,改变相对位置
|
Auxiliary 辅助显示层,初始相对 Content 位置 0,0,紧贴 Canvas, 根据 Content 滚动位置,改变相对位置
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const SimulatorContext = createContext<SimulatorHost>({} as any);
|
|
||||||
|
|
||||||
export class SimulatorHostView extends Component<SimulatorProps & {
|
export class SimulatorHostView extends Component<SimulatorProps & {
|
||||||
documentContext: DocumentModel;
|
documentContext: DocumentModel;
|
||||||
onMount?: (host: SimulatorHost) => void;
|
onMount?: (host: SimulatorHost) => void;
|
||||||
@ -32,6 +31,7 @@ export class SimulatorHostView extends Component<SimulatorProps & {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
console.info('mount simulator');
|
||||||
if (this.props.onMount) {
|
if (this.props.onMount) {
|
||||||
this.props.onMount(this.host);
|
this.props.onMount(this.host);
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ class Canvas extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div ref={elmt => sim.mountViewport(elmt)} className="lc-simulator-canvas-viewport">
|
<div ref={elmt => sim.mountViewport(elmt)} className="lc-simulator-canvas-viewport">
|
||||||
{/*<AuxiliaryView />*/}
|
<AuxiliaryView />
|
||||||
<Content />
|
<Content />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,11 +11,12 @@ import { DragObjectType, isShaken, LocateEvent, DragNodeObject, DragNodeDataObje
|
|||||||
import { LocationData } from '../../../designer/location';
|
import { LocationData } from '../../../designer/location';
|
||||||
import { NodeData } from '../../../designer/schema';
|
import { NodeData } from '../../../designer/schema';
|
||||||
import { ComponentDescriptionSpec } from '../../../designer/document/node/component-config';
|
import { ComponentDescriptionSpec } from '../../../designer/document/node/component-config';
|
||||||
|
import { ReactInstance } from 'react';
|
||||||
|
|
||||||
export interface SimulatorProps {
|
export interface SimulatorProps {
|
||||||
// 从 documentModel 上获取
|
// 从 documentModel 上获取
|
||||||
// suspended?: boolean;
|
// suspended?: boolean;
|
||||||
designMode?: 'live' | 'design' | 'extend' | 'border' | 'preview';
|
designMode?: 'live' | 'design' | 'mock' | 'extend' | 'border' | 'preview';
|
||||||
device?: 'mobile' | 'iphone' | string;
|
device?: 'mobile' | 'iphone' | string;
|
||||||
deviceClassName?: string;
|
deviceClassName?: string;
|
||||||
simulatorUrl?: Asset;
|
simulatorUrl?: Asset;
|
||||||
@ -44,6 +45,7 @@ const defaultDepends = [
|
|||||||
AssetType.JSText,
|
AssetType.JSText,
|
||||||
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
|
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
|
||||||
),
|
),
|
||||||
|
assetItem(AssetType.JSUrl, 'https://g.alicdn.com/mylib/@ali/recore/1.5.7/umd/recore.min.js'),
|
||||||
assetItem(AssetType.JSUrl, 'http://localhost:4444/js/index.js'),
|
assetItem(AssetType.JSUrl, 'http://localhost:4444/js/index.js'),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -206,7 +208,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
if (isMulti) {
|
if (isMulti) {
|
||||||
// multi select mode, directily add
|
// multi select mode, directily add
|
||||||
if (!selection.has(node.id)) {
|
if (!selection.has(node.id)) {
|
||||||
// activeTracker.track(node);
|
designer.activeTracker.track(node);
|
||||||
selection.add(node.id);
|
selection.add(node.id);
|
||||||
ignoreUpSelected = true;
|
ignoreUpSelected = true;
|
||||||
}
|
}
|
||||||
@ -265,7 +267,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const node = this.document.getNodeFromElement(e.target as Element);
|
const node = this.document.getNodeFromElement(e.target as Element);
|
||||||
hovering.hover(node, e);
|
// TODO: enhance only hover one instance
|
||||||
|
hovering.hover(node);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
const leave = () => hovering.leave(this.document);
|
const leave = () => hovering.leave(this.document);
|
||||||
@ -330,8 +333,12 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponentInstance(node: Node): ComponentInstance[] | null {
|
getComponentInstance(node: Node): ReactInstance[] | null {
|
||||||
throw new Error('Method not implemented.');
|
return this._renderer?.getComponentInstance(node.id) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentInstanceId(instance: ReactInstance) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponentContext(node: Node): object {
|
getComponentContext(node: Node): object {
|
||||||
@ -342,8 +349,59 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
return this.renderer?.getClosestNodeId(elem) || null;
|
return this.renderer?.getClosestNodeId(elem) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
findDOMNodes(instance: ComponentInstance): (Element | Text)[] | null {
|
computeComponentInstanceRect(instance: ReactInstance): DOMRect | null {
|
||||||
throw new Error('Method not implemented.');
|
const renderer = this.renderer!;
|
||||||
|
const elements = renderer.findDOMNodes(instance);
|
||||||
|
if (!elements) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rects: DOMRect[] | undefined;
|
||||||
|
let last: { x: number; y: number; r: number; b: number; } | undefined;
|
||||||
|
while (true) {
|
||||||
|
if (!rects || rects.length < 1) {
|
||||||
|
const elem = elements.pop();
|
||||||
|
if (!elem) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rects = renderer.getClientRects(elem);
|
||||||
|
}
|
||||||
|
const rect = rects.pop();
|
||||||
|
if (!rect) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!last) {
|
||||||
|
last = {
|
||||||
|
x: rect.left,
|
||||||
|
y: rect.top,
|
||||||
|
r: rect.right,
|
||||||
|
b: rect.bottom,
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (rect.left < last.x) {
|
||||||
|
last.x = rect.left;
|
||||||
|
}
|
||||||
|
if (rect.top < last.y) {
|
||||||
|
last.y = rect.top;
|
||||||
|
}
|
||||||
|
if (rect.right > last.r) {
|
||||||
|
last.r = rect.right;
|
||||||
|
}
|
||||||
|
if (rect.bottom > last.b) {
|
||||||
|
last.b = rect.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
return new DOMRect(last.x, last.y, last.r - last.x, last.b - last.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
findDOMNodes(instance: ReactInstance): Array<Element | Text> | null {
|
||||||
|
return this._renderer?.findDOMNodes(instance) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryScrollAgain: number | null = null;
|
private tryScrollAgain: number | null = null;
|
||||||
|
|||||||
@ -4,10 +4,11 @@ import { host } from './host';
|
|||||||
import SimulatorRendererView from './renderer-view';
|
import SimulatorRendererView from './renderer-view';
|
||||||
import { computed, obx } from '@recore/obx';
|
import { computed, obx } from '@recore/obx';
|
||||||
import { RootSchema, NpmInfo } from '../../../designer/schema';
|
import { RootSchema, NpmInfo } from '../../../designer/schema';
|
||||||
import { isElement } from '../../../utils/dom';
|
import { isElement, getClientRects } from '../../../utils/dom';
|
||||||
import { Asset } from '../utils/asset';
|
import { Asset } from '../utils/asset';
|
||||||
import loader from '../utils/loader';
|
import loader from '../utils/loader';
|
||||||
import { ComponentDescriptionSpec } from '../../../designer/document/node/component-config';
|
import { ComponentDescriptionSpec } from '../../../designer/document/node/component-config';
|
||||||
|
import { findDOMNodes } from '../utils/react';
|
||||||
|
|
||||||
let REACT_KEY = '';
|
let REACT_KEY = '';
|
||||||
function cacheReactKey(el: Element): Element {
|
function cacheReactKey(el: Element): Element {
|
||||||
@ -193,6 +194,14 @@ export class SimulatorRenderer {
|
|||||||
return getClosestNodeId(element);
|
return getClosestNodeId(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findDOMNodes(instance: ReactInstance): Array<Element | Text> | null {
|
||||||
|
return findDOMNodes(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
getClientRects(element: Element | Text) {
|
||||||
|
return getClientRects(element);
|
||||||
|
}
|
||||||
|
|
||||||
private _running: boolean = false;
|
private _running: boolean = false;
|
||||||
run() {
|
run() {
|
||||||
if (this._running) {
|
if (this._running) {
|
||||||
|
|||||||
@ -1,7 +1,52 @@
|
|||||||
|
@import 'variables.less';
|
||||||
|
|
||||||
.lc-designer {
|
.lc-designer {
|
||||||
|
|
||||||
|
--font-family: @font-family;
|
||||||
|
--font-size-label: @fontSize-4;
|
||||||
|
--font-size-text: @fontSize-5;
|
||||||
|
--font-size-btn-large: @fontSize-3;
|
||||||
|
--font-size-btn-medium: @fontSize-4;
|
||||||
|
--font-size-btn-small: @fontSize-5;
|
||||||
|
|
||||||
|
--color-brand-light: rgb(102, 188, 92);
|
||||||
|
--color-icon: rgba(255, 255, 255, 0.8);
|
||||||
|
--color-visited: rgba(179, 182, 201, 0.4);
|
||||||
|
--color-actived: #498ee6;
|
||||||
|
|
||||||
|
--color-border: @white-alpha-7;
|
||||||
|
--color-btn: #0079F2;
|
||||||
|
--color-btn-border: rgba(0, 121, 242, 0.3);
|
||||||
|
--color-btn-bg: #212938;
|
||||||
|
|
||||||
|
--color-form-bg: #272A35;
|
||||||
|
--color-form-border: rgba(63,70,93,1);
|
||||||
|
|
||||||
|
--color-text: @white-alpha-3;
|
||||||
|
--color-text-light: @white-alpha-1;
|
||||||
|
--color-field-placeholder: @white-alpha-5;
|
||||||
|
--color-pane-label: rgba(255, 255, 255, 0.9);
|
||||||
|
--color-border: rgba(63, 70, 93, 1);
|
||||||
|
--color-field-border: rgba(118, 137, 199, 0.6);
|
||||||
|
--color-function-warning: rgb(204, 131, 98);
|
||||||
|
--color-field-border-hover: rgb(118, 137, 199, 0.8);
|
||||||
|
--color-field-border-active: rgb(118, 137, 199);
|
||||||
|
--color-block-background-disabled: rgba(118, 137, 199, 0.35);
|
||||||
|
|
||||||
|
--global-border-radius: @global-border-radius;
|
||||||
|
--input-border-radius: @input-border-radius;
|
||||||
|
--popup-border-radius: @popup-border-radius;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
font-size: var(--font-size-text);
|
||||||
min-width: 500px;
|
min-width: 500px;
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
.lc-project {
|
.lc-project {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import Node, { insertChildren } from './document/node/node';
|
|||||||
import { isRootNode } from './document/node/root-node';
|
import { isRootNode } from './document/node/root-node';
|
||||||
import { ComponentDescriptionSpec, ComponentConfig } from './document/node/component-config';
|
import { ComponentDescriptionSpec, ComponentConfig } from './document/node/component-config';
|
||||||
import Scroller, { IScrollable } from './scroller';
|
import Scroller, { IScrollable } from './scroller';
|
||||||
|
import { INodeInstance } from './simulator';
|
||||||
|
import OffsetObserver, { createOffsetObserver } from './offset-observer';
|
||||||
|
|
||||||
export interface DesignerProps {
|
export interface DesignerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -118,6 +120,10 @@ export default class Designer {
|
|||||||
return new Scroller(scrollable);
|
return new Scroller(scrollable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createOffsetObserver(nodeInstance: INodeInstance): OffsetObserver | null {
|
||||||
|
return createOffsetObserver(nodeInstance);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得合适的插入位置
|
* 获得合适的插入位置
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -49,7 +49,7 @@ export default class DocumentModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(readonly project: Project, schema: RootSchema) {
|
constructor(readonly project: Project, schema: RootSchema) {
|
||||||
this.rootNode = new RootNode(this, schema);
|
this.rootNode = this.createNode(schema) as RootNode;
|
||||||
this.id = this.rootNode.id;
|
this.id = this.rootNode.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { obx } from '@recore/obx';
|
import { obx, computed } from '@recore/obx';
|
||||||
import { NodeSchema, NodeData, PropsMap, PropsList } from '../../schema';
|
import { NodeSchema, NodeData, PropsMap, PropsList } from '../../schema';
|
||||||
import Props from './props/props';
|
import Props from './props/props';
|
||||||
import DocumentModel from '../document-model';
|
import DocumentModel from '../document-model';
|
||||||
@ -24,10 +24,10 @@ const DIRECTIVES = ['condition', 'conditionGroup', 'loop', 'loopArgs', 'title',
|
|||||||
* condition
|
* condition
|
||||||
* ------- future support -----
|
* ------- future support -----
|
||||||
* conditionGroup
|
* conditionGroup
|
||||||
* title
|
* x-title
|
||||||
* ignore
|
* x-ignore
|
||||||
* locked
|
* x-locked
|
||||||
* hidden
|
* x-hidden
|
||||||
*/
|
*/
|
||||||
export default class Node {
|
export default class Node {
|
||||||
/**
|
/**
|
||||||
@ -84,6 +84,20 @@ export default class Node {
|
|||||||
return this._zLevel;
|
return this._zLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@computed get title(): string {
|
||||||
|
let t = this.getDirective('x-title');
|
||||||
|
if (!t && this.componentConfig.descriptor) {
|
||||||
|
t = this.getProp(this.componentConfig.descriptor, false);
|
||||||
|
}
|
||||||
|
if (t) {
|
||||||
|
const v = t.getAsString();
|
||||||
|
if (v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.componentName;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(readonly document: DocumentModel, nodeSchema: NodeSchema) {
|
constructor(readonly document: DocumentModel, nodeSchema: NodeSchema) {
|
||||||
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()}`;
|
||||||
|
|||||||
@ -62,6 +62,13 @@ export default class Prop implements IPropParent {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@computed getAsString(): string {
|
||||||
|
if (this.type === 'literal') {
|
||||||
|
return this._value ? String(this._value) : '';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set value, val should be JSON Object
|
* set value, val should be JSON Object
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,25 +10,24 @@ export default class Hovering {
|
|||||||
set enable(flag: boolean) {
|
set enable(flag: boolean) {
|
||||||
this._enable = flag;
|
this._enable = flag;
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
this._hovering = null;
|
this._current = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@obx.ref xRayMode: boolean = false;
|
@obx.ref xRayMode: boolean = false;
|
||||||
|
|
||||||
@obx.ref private _hovering: Node | null = null;
|
@obx.ref private _current: Node | null = null;
|
||||||
get hovering() {
|
get current() {
|
||||||
return this._hovering;
|
return this._current;
|
||||||
}
|
}
|
||||||
|
|
||||||
@obx.ref event?: MouseEvent;
|
hover(node: Node | null) {
|
||||||
hover(node: Node | null, e: MouseEvent) {
|
console.info(node);
|
||||||
this._hovering = node;
|
this._current = node;
|
||||||
this.event = e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
leave(document: DocumentModel) {
|
leave(document: DocumentModel) {
|
||||||
if (this.hovering && this.hovering.document === document) {
|
if (this.current && this.current.document === document) {
|
||||||
this._hovering = null;
|
this._current = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
75
packages/designer/src/designer/offset-observer.ts
Normal file
75
packages/designer/src/designer/offset-observer.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { obx, computed } from '@recore/obx';
|
||||||
|
import { INodeInstance, IViewport } from './simulator';
|
||||||
|
import Viewport from '../builtins/simulator/host/viewport';
|
||||||
|
|
||||||
|
export default class OffsetObserver {
|
||||||
|
@obx.ref hasOffset = false;
|
||||||
|
|
||||||
|
@computed get offsetLeft() {
|
||||||
|
return this.left + this.viewport.scrollX;
|
||||||
|
}
|
||||||
|
@computed get offsetTop() {
|
||||||
|
return this.top + this.viewport.scrollY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@obx.ref height = 0;
|
||||||
|
@obx.ref width = 0;
|
||||||
|
@obx.ref left = 0;
|
||||||
|
@obx.ref top = 0;
|
||||||
|
|
||||||
|
@computed get scale() {
|
||||||
|
return this.viewport.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
private pid: number | undefined;
|
||||||
|
private viewport: IViewport;
|
||||||
|
|
||||||
|
constructor(readonly nodeInstance: INodeInstance) {
|
||||||
|
const { node, instance } = nodeInstance;
|
||||||
|
const doc = node.document;
|
||||||
|
const host = doc.simulator!;
|
||||||
|
this.viewport = host.viewport;
|
||||||
|
if (!instance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pid: number;
|
||||||
|
const compute = () => {
|
||||||
|
if (pid !== this.pid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = host.computeComponentInstanceRect(instance!);
|
||||||
|
|
||||||
|
if (!rect) {
|
||||||
|
this.hasOffset = false;
|
||||||
|
} else {
|
||||||
|
this.hasOffset = true;
|
||||||
|
this.height = rect.height;
|
||||||
|
this.width = rect.width;
|
||||||
|
this.left = rect.left;
|
||||||
|
this.top = rect.top;
|
||||||
|
}
|
||||||
|
this.pid = pid = (window as any).requestIdleCallback(compute);
|
||||||
|
};
|
||||||
|
|
||||||
|
// try first
|
||||||
|
compute();
|
||||||
|
// try second, ensure the dom mounted
|
||||||
|
this.pid = pid = (window as any).requestIdleCallback(compute);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this.pid) {
|
||||||
|
(window as any).cancelIdleCallback(this.pid);
|
||||||
|
}
|
||||||
|
this.pid = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createOffsetObserver(nodeInstance: INodeInstance): OffsetObserver | null {
|
||||||
|
if (!nodeInstance.instance) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new OffsetObserver(nodeInstance);
|
||||||
|
}
|
||||||
@ -136,6 +136,8 @@ export interface ISimulator<P = object> extends ISensor {
|
|||||||
|
|
||||||
getClosestNodeId(elem: Element): string | null;
|
getClosestNodeId(elem: Element): string | null;
|
||||||
|
|
||||||
|
computeComponentInstanceRect(instance: ComponentInstance): DOMRect | null;
|
||||||
|
|
||||||
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null;
|
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null;
|
||||||
|
|
||||||
setSuspense(suspensed: boolean): void;
|
setSuspense(suspensed: boolean): void;
|
||||||
@ -154,3 +156,8 @@ export type Component = ComponentType<any> | object;
|
|||||||
* 组件实例定义
|
* 组件实例定义
|
||||||
*/
|
*/
|
||||||
export type ComponentInstance = Element | ReactComponent<any> | object;
|
export type ComponentInstance = Element | ReactComponent<any> | object;
|
||||||
|
|
||||||
|
export interface INodeInstance {
|
||||||
|
node: Node;
|
||||||
|
instance?: ComponentInstance;
|
||||||
|
}
|
||||||
|
|||||||
170
packages/designer/src/designer/variables.less
Normal file
170
packages/designer/src/designer/variables.less
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* 基础的 DPL 定义使用了 kuma base 的定义,参考:
|
||||||
|
* https://github.com/uxcore/kuma-base/tree/master/variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ===========================================================
|
||||||
|
* ==================== Font Family ==========================
|
||||||
|
* ===========================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @font-family: "STHeiti", "Microsoft Yahei", "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
|
||||||
|
*/
|
||||||
|
|
||||||
|
@font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
|
||||||
|
@font-family-code: Monaco, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ===========================================================
|
||||||
|
* ===================== Color DPL ===========================
|
||||||
|
* ===========================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
@brand-color-1: rgba(0, 108, 255, 1);
|
||||||
|
@brand-color-2: rgba(25, 122, 255, 1);
|
||||||
|
@brand-color-3: rgba(0, 96, 229, 1);
|
||||||
|
|
||||||
|
@brand-color-1-3: rgba(0, 108, 255, 0.6);
|
||||||
|
@brand-color-1-4: rgba(0, 108, 255, 0.4);
|
||||||
|
@brand-color-1-5: rgba(0, 108, 255, 0.3);
|
||||||
|
@brand-color-1-6: rgba(0, 108, 255, 0.2);
|
||||||
|
@brand-color-1-7: rgba(0, 108, 255, 0.1);
|
||||||
|
|
||||||
|
@brand-color: @brand-color-1;
|
||||||
|
|
||||||
|
@white-alpha-1: rgb(255, 255, 255); // W-1
|
||||||
|
@white-alpha-2: rgba(255, 255, 255, 0.8); // W-2 A80
|
||||||
|
@white-alpha-3: rgba(255, 255, 255, 0.6); // W-3 A60
|
||||||
|
@white-alpha-4: rgba(255, 255, 255, 0.4); // W-4 A40
|
||||||
|
@white-alpha-5: rgba(255, 255, 255, 0.3); // W-5 A30
|
||||||
|
@white-alpha-6: rgba(255, 255, 255, 0.2); // W-6 A20
|
||||||
|
@white-alpha-7: rgba(255, 255, 255, 0.1); // W-7 A10
|
||||||
|
@white-alpha-8: rgba(255, 255, 255, 0.06); // W-8 A6
|
||||||
|
|
||||||
|
@dark-alpha-1: rgba(0, 0, 0, 1); // D-1 A100
|
||||||
|
@dark-alpha-2: rgba(0, 0, 0, 0.8); // D-2 A80
|
||||||
|
@dark-alpha-3: rgba(0, 0, 0, 0.6); // D-3 A60
|
||||||
|
@dark-alpha-4: rgba(0, 0, 0, 0.4); // D-4 A40
|
||||||
|
@dark-alpha-5: rgba(0, 0, 0, 0.3); // D-5 A30
|
||||||
|
@dark-alpha-6: rgba(0, 0, 0, 0.2); // D-6 A20
|
||||||
|
@dark-alpha-7: rgba(0, 0, 0, 0.1); // D-7 A10
|
||||||
|
@dark-alpha-8: rgba(0, 0, 0, 0.06); // D-8 A6
|
||||||
|
@dark-alpha-9: rgba(0, 0, 0, 0.04); // D-9 A4
|
||||||
|
|
||||||
|
@normal-alpha-1: rgba(31, 56, 88, 1); // N-1 A100
|
||||||
|
@normal-alpha-2: rgba(31, 56, 88, 0.8); // N-2 A80
|
||||||
|
@normal-alpha-3: rgba(31, 56, 88, 0.6); // N-3 A60
|
||||||
|
@normal-alpha-4: rgba(31, 56, 88, 0.4); // N-4 A40
|
||||||
|
@normal-alpha-5: rgba(31, 56, 88, 0.3); // N-5 A30
|
||||||
|
@normal-alpha-6: rgba(31, 56, 88, 0.2); // N-6 A20
|
||||||
|
@normal-alpha-7: rgba(31, 56, 88, 0.1); // N-7 A10
|
||||||
|
@normal-alpha-8: rgba(31, 56, 88, 0.06); // N-8 A6
|
||||||
|
@normal-alpha-9: rgba(31, 56, 88, 0.04); // N-9 A4
|
||||||
|
|
||||||
|
@normal-3: #77879c;
|
||||||
|
@normal-4: #a3aebd;
|
||||||
|
@normal-5: #bac3cc;
|
||||||
|
@normal-6: #d1d7de;
|
||||||
|
|
||||||
|
@gray-dark: #333; // N2_4
|
||||||
|
@gray: #666; // N2_3
|
||||||
|
@gray-light: #999; // N2_2
|
||||||
|
@gray-lighter: #ccc; // N2_1
|
||||||
|
|
||||||
|
@brand-secondary: #2c2f33; // B2_3
|
||||||
|
// 补色
|
||||||
|
@brand-complement: #00b3e8; // B3_1
|
||||||
|
// 复合
|
||||||
|
@brand-comosite: #00c587; // B3_2
|
||||||
|
// 浓度
|
||||||
|
@brand-deep: #73461d; // B3_3
|
||||||
|
|
||||||
|
// F1-1
|
||||||
|
@brand-danger: rgb(240, 70, 49);
|
||||||
|
// F1-2 (10% white)
|
||||||
|
@brand-danger-hover: rgba(240, 70, 49, 0.9);
|
||||||
|
// F1-3 (5% black)
|
||||||
|
@brand-danger-focus: rgba(240, 70, 49, 0.95);
|
||||||
|
|
||||||
|
// F2-1
|
||||||
|
@brand-warning: rgb(250, 189, 14);
|
||||||
|
// F3-1
|
||||||
|
@brand-success: rgb(102, 188, 92);
|
||||||
|
// F4-1
|
||||||
|
@brand-link: rgb(102, 188, 92);
|
||||||
|
// F4-2
|
||||||
|
@brand-link-hover: #2e76a6;
|
||||||
|
|
||||||
|
// F1-1-7 A10
|
||||||
|
@brand-danger-alpha-7: rgba(240, 70, 49, 0.9);
|
||||||
|
// F1-1-8 A6
|
||||||
|
@brand-danger-alpha-8: rgba(240, 70, 49, 0.8);
|
||||||
|
// F2-1-2 A80
|
||||||
|
@brand-warning-alpha-2: rgba(250, 189, 14, 0.8);
|
||||||
|
// F2-1-7 A10
|
||||||
|
@brand-warning-alpha-7: rgba(250, 189, 14, 0.9);
|
||||||
|
// F3-1-2 A80
|
||||||
|
@brand-success-alpha-2: rgba(102, 188, 92, 0.8);
|
||||||
|
// F3-1-7 A10
|
||||||
|
@brand-success-alpha-7: rgba(102, 188, 92, 0.9);
|
||||||
|
// F4-1-7 A10
|
||||||
|
@brand-link-alpha-7: rgba(102, 188, 92, 0.9);
|
||||||
|
|
||||||
|
// 文本色
|
||||||
|
@text-primary-color: @dark-alpha-3;
|
||||||
|
@text-secondary-color: @normal-alpha-3;
|
||||||
|
@text-thirdary-color: @dark-alpha-4;
|
||||||
|
@text-disabled-color: @normal-alpha-5;
|
||||||
|
@text-helper-color: @dark-alpha-4;
|
||||||
|
@text-danger-color: @brand-danger;
|
||||||
|
@text-ali-color: #ec6c00;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ===========================================================
|
||||||
|
* =================== Shadow Box ============================
|
||||||
|
* ===========================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
@box-shadow-1: 0 1px 4px 0 rgba(31, 56, 88, 0.15); // 1 级阴影,物体由原来存在于底面的物体展开,物体和底面关联紧密
|
||||||
|
@box-shadow-2: 0 2px 10px 0 rgba(31, 56, 88, 0.15); // 2 级阴影,hover状态,物体层级较高
|
||||||
|
@box-shadow-3: 0 4px 15px 0 rgba(31, 56, 88, 0.15); // 3 级阴影,当物体层级高于所有界面元素,弹窗用
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ===========================================================
|
||||||
|
* ================= FontSize of Level =======================
|
||||||
|
* ===========================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
@fontSize-1: 26px;
|
||||||
|
@fontSize-2: 20px;
|
||||||
|
@fontSize-3: 16px;
|
||||||
|
@fontSize-4: 14px;
|
||||||
|
@fontSize-5: 12px;
|
||||||
|
|
||||||
|
@fontLineHeight-1: 38px;
|
||||||
|
@fontLineHeight-2: 30px;
|
||||||
|
@fontLineHeight-3: 26px;
|
||||||
|
@fontLineHeight-4: 24px;
|
||||||
|
@fontLineHeight-5: 20px;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ===========================================================
|
||||||
|
* ================= FontSize of Level =======================
|
||||||
|
* ===========================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
@global-border-radius: 3px;
|
||||||
|
@input-border-radius: 3px;
|
||||||
|
@popup-border-radius: 6px;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ===========================================================
|
||||||
|
* ===================== Transistion =========================
|
||||||
|
* ===========================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
@transition-duration: 0.3s;
|
||||||
|
@transition-ease: cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
@transition-delay: 0s;
|
||||||
Loading…
x
Reference in New Issue
Block a user