| null {
+ return findDOMNodes(instance);
+ }
+
+ getClientRects(element: Element | Text) {
+ return getClientRects(element);
+ }
+
private _running: boolean = false;
run() {
if (this._running) {
diff --git a/packages/designer/src/designer/designer.less b/packages/designer/src/designer/designer.less
index c36bc5eab..2c0631786 100644
--- a/packages/designer/src/designer/designer.less
+++ b/packages/designer/src/designer/designer.less
@@ -1,7 +1,52 @@
+@import 'variables.less';
+
.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;
+ font-family: var(--font-family);
+ font-size: var(--font-size-text);
min-width: 500px;
min-height: 500px;
+ box-sizing: border-box;
+
+ * {
+ box-sizing: border-box;
+ }
.lc-project {
position: absolute;
top: 0;
diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts
index b2ec25c2d..1ccdaf127 100644
--- a/packages/designer/src/designer/designer.ts
+++ b/packages/designer/src/designer/designer.ts
@@ -12,6 +12,8 @@ import Node, { insertChildren } from './document/node/node';
import { isRootNode } from './document/node/root-node';
import { ComponentDescriptionSpec, ComponentConfig } from './document/node/component-config';
import Scroller, { IScrollable } from './scroller';
+import { INodeInstance } from './simulator';
+import OffsetObserver, { createOffsetObserver } from './offset-observer';
export interface DesignerProps {
className?: string;
@@ -118,6 +120,10 @@ export default class Designer {
return new Scroller(scrollable);
}
+ createOffsetObserver(nodeInstance: INodeInstance): OffsetObserver | null {
+ return createOffsetObserver(nodeInstance);
+ }
+
/**
* 获得合适的插入位置
*/
diff --git a/packages/designer/src/designer/document/document-model.ts b/packages/designer/src/designer/document/document-model.ts
index ee72f214c..3f9d7d64e 100644
--- a/packages/designer/src/designer/document/document-model.ts
+++ b/packages/designer/src/designer/document/document-model.ts
@@ -49,7 +49,7 @@ export default class DocumentModel {
}
constructor(readonly project: Project, schema: RootSchema) {
- this.rootNode = new RootNode(this, schema);
+ this.rootNode = this.createNode(schema) as RootNode;
this.id = this.rootNode.id;
}
diff --git a/packages/designer/src/designer/document/node/node.ts b/packages/designer/src/designer/document/node/node.ts
index 81ca45831..f913b15eb 100644
--- a/packages/designer/src/designer/document/node/node.ts
+++ b/packages/designer/src/designer/document/node/node.ts
@@ -1,4 +1,4 @@
-import { obx } from '@recore/obx';
+import { obx, computed } from '@recore/obx';
import { NodeSchema, NodeData, PropsMap, PropsList } from '../../schema';
import Props from './props/props';
import DocumentModel from '../document-model';
@@ -24,10 +24,10 @@ const DIRECTIVES = ['condition', 'conditionGroup', 'loop', 'loopArgs', 'title',
* condition
* ------- future support -----
* conditionGroup
- * title
- * ignore
- * locked
- * hidden
+ * x-title
+ * x-ignore
+ * x-locked
+ * x-hidden
*/
export default class Node {
/**
@@ -84,6 +84,20 @@ export default class Node {
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) {
const { componentName, id, children, props, ...extras } = nodeSchema;
this.id = id || `node$${document.nextId()}`;
diff --git a/packages/designer/src/designer/document/node/props/prop.ts b/packages/designer/src/designer/document/node/props/prop.ts
index 9986884d7..274c56d90 100644
--- a/packages/designer/src/designer/document/node/props/prop.ts
+++ b/packages/designer/src/designer/document/node/props/prop.ts
@@ -62,6 +62,13 @@ export default class Prop implements IPropParent {
return null;
}
+ @computed getAsString(): string {
+ if (this.type === 'literal') {
+ return this._value ? String(this._value) : '';
+ }
+ return '';
+ }
+
/**
* set value, val should be JSON Object
*/
diff --git a/packages/designer/src/designer/hovering.ts b/packages/designer/src/designer/hovering.ts
index 641b8e3a7..4c0cd0d9c 100644
--- a/packages/designer/src/designer/hovering.ts
+++ b/packages/designer/src/designer/hovering.ts
@@ -10,25 +10,24 @@ export default class Hovering {
set enable(flag: boolean) {
this._enable = flag;
if (!flag) {
- this._hovering = null;
+ this._current = null;
}
}
@obx.ref xRayMode: boolean = false;
- @obx.ref private _hovering: Node | null = null;
- get hovering() {
- return this._hovering;
+ @obx.ref private _current: Node | null = null;
+ get current() {
+ return this._current;
}
- @obx.ref event?: MouseEvent;
- hover(node: Node | null, e: MouseEvent) {
- this._hovering = node;
- this.event = e;
+ hover(node: Node | null) {
+ console.info(node);
+ this._current = node;
}
leave(document: DocumentModel) {
- if (this.hovering && this.hovering.document === document) {
- this._hovering = null;
+ if (this.current && this.current.document === document) {
+ this._current = null;
}
}
}
diff --git a/packages/designer/src/designer/offset-observer.ts b/packages/designer/src/designer/offset-observer.ts
new file mode 100644
index 000000000..7b29d572e
--- /dev/null
+++ b/packages/designer/src/designer/offset-observer.ts
@@ -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);
+}
diff --git a/packages/designer/src/designer/simulator.ts b/packages/designer/src/designer/simulator.ts
index 5d036c458..07dfc5635 100644
--- a/packages/designer/src/designer/simulator.ts
+++ b/packages/designer/src/designer/simulator.ts
@@ -136,6 +136,8 @@ export interface ISimulator extends ISensor {
getClosestNodeId(elem: Element): string | null;
+ computeComponentInstanceRect(instance: ComponentInstance): DOMRect | null;
+
findDOMNodes(instance: ComponentInstance): Array | null;
setSuspense(suspensed: boolean): void;
@@ -154,3 +156,8 @@ export type Component = ComponentType | object;
* 组件实例定义
*/
export type ComponentInstance = Element | ReactComponent | object;
+
+export interface INodeInstance {
+ node: Node;
+ instance?: ComponentInstance;
+}
diff --git a/packages/designer/src/designer/variables.less b/packages/designer/src/designer/variables.less
new file mode 100644
index 000000000..f61f2341c
--- /dev/null
+++ b/packages/designer/src/designer/variables.less
@@ -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;