mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-02-21 08:20:27 +00:00
complete outline-ui
This commit is contained in:
parent
dc8fdae09d
commit
9a2e5e97de
@ -1,5 +1,5 @@
|
||||
import { Component } from 'react';
|
||||
import { observer, obx } from '../../../../globals';
|
||||
import { observer, obx, Title } from '../../../../globals';
|
||||
import Designer from '../../designer/designer';
|
||||
import { isDragNodeObject, DragObject, isDragNodeDataObject } from '../../designer/helper/dragon';
|
||||
import './ghost.less';
|
||||
@ -53,7 +53,7 @@ export default class Ghost extends Component<{ designer: Designer }> {
|
||||
return dragObject.nodes.map(node => {
|
||||
const ghost = (
|
||||
<div className="lc-ghost" key={node.id}>
|
||||
<div className="lc-ghost-title">{node.title}</div>
|
||||
<Title title={node.title} />
|
||||
</div>
|
||||
);
|
||||
return ghost;
|
||||
|
||||
@ -108,7 +108,8 @@ export class ComponentMeta {
|
||||
return this._title || this.componentName;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
@computed get icon() {
|
||||
// give Slot default icon
|
||||
return (
|
||||
this._transformedMetadata?.icon ||
|
||||
(this.componentName === 'Page' ? IconPage : this.isContainer ? IconContainer : IconComponent)
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
import { obx, computed, TitleContent } from '../../../../../globals';
|
||||
import { uniqueId } from '../../../../../utils/unique-id';
|
||||
import Node from './node';
|
||||
import { intl } from '../../../locale';
|
||||
|
||||
export default class ExclusiveGroup {
|
||||
readonly isExclusiveGroup = true;
|
||||
readonly id = uniqueId('cond-grp');
|
||||
@obx.val readonly children: Node[] = [];
|
||||
|
||||
@obx private visibleIndex = 0;
|
||||
@computed get document() {
|
||||
return this.visibleNode.document;
|
||||
}
|
||||
@computed get zLevel() {
|
||||
return this.visibleNode.zLevel;
|
||||
}
|
||||
@computed get length() {
|
||||
return this.children.length;
|
||||
}
|
||||
@computed get visibleNode(): Node {
|
||||
return this.children[this.visibleIndex];
|
||||
}
|
||||
@computed get firstNode(): Node {
|
||||
return this.children[0]!;
|
||||
}
|
||||
|
||||
add(node: Node) {
|
||||
if (node.nextSibling && node.nextSibling.conditionGroup === this) {
|
||||
const i = this.children.indexOf(node.nextSibling);
|
||||
this.children.splice(i, 0, node);
|
||||
} else {
|
||||
this.children.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
remove(node: Node) {
|
||||
const i = this.children.indexOf(node);
|
||||
if (i > -1) {
|
||||
this.children.splice(i, 1);
|
||||
if (this.visibleIndex > i) {
|
||||
this.visibleIndex -= 1;
|
||||
} else if (this.visibleIndex >= this.children.length) {
|
||||
this.visibleIndex = this.children.length - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setVisible(node: Node) {
|
||||
const i = this.children.indexOf(node);
|
||||
if (i > -1) {
|
||||
this.visibleIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(node: Node) {
|
||||
const i = this.children.indexOf(node);
|
||||
return i === this.visibleIndex;
|
||||
}
|
||||
|
||||
readonly title: TitleContent;
|
||||
|
||||
constructor(readonly name: string, title?: TitleContent) {
|
||||
this.title = title || {
|
||||
type: 'i18n',
|
||||
intl: intl('Condition Group'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function isExclusiveGroup(obj: any): obj is ExclusiveGroup {
|
||||
return obj && obj.isExclusiveGroup;
|
||||
}
|
||||
@ -5,12 +5,14 @@ export default class NodeChildren {
|
||||
@obx.val private children: Node[];
|
||||
constructor(readonly owner: NodeParent, data: NodeData | NodeData[]) {
|
||||
this.children = (Array.isArray(data) ? data : [data]).map(child => {
|
||||
const node = this.owner.document.createNode(child);
|
||||
node.internalSetParent(this.owner);
|
||||
return node;
|
||||
return this.owner.document.createNode(child);
|
||||
});
|
||||
}
|
||||
|
||||
interalInitParent() {
|
||||
this.children.forEach(child => child.internalSetParent(this.owner));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出 schema
|
||||
* @param serialize 序列化,加 id 标识符,用于储存为操作记录
|
||||
@ -109,11 +111,24 @@ export default class NodeChildren {
|
||||
}
|
||||
|
||||
// check condition group
|
||||
node.conditionGroup = null;
|
||||
if (node.conditionGroup) {
|
||||
if (
|
||||
!(
|
||||
// just sort at condition group
|
||||
(
|
||||
(node.prevSibling && node.prevSibling.conditionGroup === node.conditionGroup) ||
|
||||
(node.nextSibling && node.nextSibling.conditionGroup === node.conditionGroup)
|
||||
)
|
||||
)
|
||||
) {
|
||||
node.setConditionGroup(null);
|
||||
}
|
||||
}
|
||||
if (node.prevSibling && node.nextSibling) {
|
||||
const conditionGroup = node.prevSibling.conditionGroup;
|
||||
// insert at condition group
|
||||
if (conditionGroup && conditionGroup === node.nextSibling.conditionGroup) {
|
||||
node.conditionGroup = conditionGroup;
|
||||
node.setConditionGroup(conditionGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
obx,
|
||||
computed,
|
||||
} from '../../../../../globals';
|
||||
import ExclusiveGroup, { isExclusiveGroup } from './exclusive-group';
|
||||
|
||||
/**
|
||||
* 基础节点
|
||||
@ -29,10 +30,10 @@ import {
|
||||
* condition
|
||||
* ------- future support -----
|
||||
* conditionGroup
|
||||
* x-title
|
||||
* x-ignore
|
||||
* x-locked
|
||||
* x-hidden
|
||||
* title
|
||||
* ignored
|
||||
* locked
|
||||
* hidden
|
||||
*/
|
||||
export default class Node {
|
||||
/**
|
||||
@ -80,7 +81,7 @@ export default class Node {
|
||||
if (this._parent) {
|
||||
return this._parent.zLevel + 1;
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@computed get title(): TitleContent {
|
||||
@ -110,6 +111,7 @@ export default class Node {
|
||||
if (isNodeParent(this)) {
|
||||
_props = new Props(this, props, extras);
|
||||
this._children = new NodeChildren(this as NodeParent, children || []);
|
||||
this._children.interalInitParent();
|
||||
} else {
|
||||
_props = new Props(this, {
|
||||
children: isDOMText(children) || isJSExpression(children) ? children : '',
|
||||
@ -138,6 +140,13 @@ export default class Node {
|
||||
}
|
||||
|
||||
this._parent = parent;
|
||||
if (parent && !this.conditionGroup) {
|
||||
// initial conditionGroup
|
||||
const grp = this.getExtraProp('conditionGroup', false)?.getAsString();
|
||||
if (grp) {
|
||||
this.setConditionGroup(grp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _slotFor?: Prop | null = null;
|
||||
@ -214,37 +223,53 @@ export default class Node {
|
||||
return slots;
|
||||
}
|
||||
|
||||
private _conditionGroup: string | null = null;
|
||||
/**
|
||||
* 条件组
|
||||
*/
|
||||
get conditionGroup(): string | null {
|
||||
if (this._conditionGroup) {
|
||||
@obx.ref private _conditionGroup: ExclusiveGroup | null = null;
|
||||
get conditionGroup(): ExclusiveGroup | null {
|
||||
return this._conditionGroup;
|
||||
}
|
||||
// 如果 condition 有值,且没有 group
|
||||
if (this._condition) {
|
||||
return this.id;
|
||||
|
||||
setConditionGroup(grp: ExclusiveGroup | string | null) {
|
||||
if (!grp) {
|
||||
this.getExtraProp('conditionGroup', false)?.remove();
|
||||
if (this._conditionGroup) {
|
||||
this._conditionGroup.remove(this);
|
||||
this._conditionGroup = null;
|
||||
}
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
if (!isExclusiveGroup(grp)) {
|
||||
if (this.prevSibling?.conditionGroup?.name === grp) {
|
||||
grp = this.prevSibling.conditionGroup;
|
||||
} else {
|
||||
grp = new ExclusiveGroup(grp);
|
||||
}
|
||||
}
|
||||
if (this._conditionGroup !== grp) {
|
||||
this.getExtraProp('conditionGroup', true)?.setValue(grp.name);
|
||||
if (this._conditionGroup) {
|
||||
this._conditionGroup.remove(this);
|
||||
}
|
||||
this._conditionGroup = grp;
|
||||
grp.add(this);
|
||||
}
|
||||
set conditionGroup(val) {
|
||||
this._conditionGroup = val;
|
||||
}
|
||||
|
||||
private _condition: any;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get condition() {
|
||||
if (this._condition == null) {
|
||||
if (this._conditionGroup) {
|
||||
// FIXME: should be expression
|
||||
return true;
|
||||
@computed isConditionalVisible(): boolean | undefined {
|
||||
return this._conditionGroup?.isVisible(this);
|
||||
}
|
||||
return null;
|
||||
|
||||
setConditionalVisible() {
|
||||
this._conditionGroup?.setVisible(this);
|
||||
}
|
||||
return this._condition;
|
||||
|
||||
@computed hasCondition() {
|
||||
const v = this.getExtraProp('condition', false)?.getValue();
|
||||
return v != null && v !== '';
|
||||
}
|
||||
|
||||
@computed hasLoop() {
|
||||
const v = this.getExtraProp('loop', false)?.getValue();
|
||||
return v != null && v !== '';
|
||||
}
|
||||
|
||||
wrapWith(schema: NodeSchema) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"copy": "Copy",
|
||||
"remove": "Remove"
|
||||
"remove": "Remove",
|
||||
"Condition Group": "Condition Group"
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"copy": "复制",
|
||||
"remove": "删除"
|
||||
"remove": "删除",
|
||||
"Condition Group": "条件组"
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import undoRedo from '../plugins/undoRedo';
|
||||
import Designer from '../plugins/designer';
|
||||
import logo from '../plugins/logo';
|
||||
import save from '../plugins/save';
|
||||
import OutlineTree from '../../../plugin-outline-tree';
|
||||
|
||||
import PluginFactory from '../framework/pluginFactory';
|
||||
|
||||
@ -22,6 +23,7 @@ export default {
|
||||
save: PluginFactory(save),
|
||||
designer: PluginFactory(Designer),
|
||||
settings: PluginFactory(Settings),
|
||||
outlineTree: PluginFactory(OutlineTree),
|
||||
undoRedo: PluginFactory(undoRedo),
|
||||
topBalloonIcon: PluginFactory(topBalloonIcon),
|
||||
topDialogIcon: PluginFactory(topDialogIcon),
|
||||
|
||||
@ -238,6 +238,7 @@ export default {
|
||||
}
|
||||
],
|
||||
rightArea: [
|
||||
/*
|
||||
{
|
||||
pluginKey: 'settings',
|
||||
type: 'Panel',
|
||||
@ -246,52 +247,16 @@ export default {
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},*/
|
||||
{
|
||||
pluginKey: 'outlineTree',
|
||||
type: 'Panel',
|
||||
props: {},
|
||||
config: {
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
}
|
||||
// {
|
||||
// pluginKey: 'rightPanel1',
|
||||
// type: 'TabPanel',
|
||||
// props: {
|
||||
// title: '样式'
|
||||
// },
|
||||
// config: {
|
||||
// version: '^1.0.0'
|
||||
// },
|
||||
// pluginProps: {}
|
||||
// },
|
||||
// {
|
||||
// pluginKey: 'rightPanel2',
|
||||
// type: 'TabPanel',
|
||||
// props: {
|
||||
// title: '属性',
|
||||
// icon: 'dengpao'
|
||||
// },
|
||||
// config: {
|
||||
// version: '^1.0.0'
|
||||
// },
|
||||
// pluginProps: {}
|
||||
// },
|
||||
// {
|
||||
// pluginKey: 'rightPanel3',
|
||||
// type: 'TabPanel',
|
||||
// props: {
|
||||
// title: '事件'
|
||||
// },
|
||||
// config: {
|
||||
// version: '^1.0.0'
|
||||
// },
|
||||
// pluginProps: {}
|
||||
// },
|
||||
// {
|
||||
// pluginKey: 'rightPanel4',
|
||||
// type: 'TabPanel',
|
||||
// props: {
|
||||
// title: '数据'
|
||||
// },
|
||||
// config: {
|
||||
// version: '^1.0.0'
|
||||
// },
|
||||
// pluginProps: {}
|
||||
// }
|
||||
],
|
||||
centerArea: [
|
||||
{
|
||||
|
||||
@ -81,7 +81,13 @@ const SCHEMA = {
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
props: {
|
||||
label: '职业:',
|
||||
label: {
|
||||
type: 'JSSlot',
|
||||
value: {
|
||||
componentName: 'Div',
|
||||
children: '职业:',
|
||||
}
|
||||
},
|
||||
name: 'profession'
|
||||
},
|
||||
children: [
|
||||
@ -127,7 +133,10 @@ const SCHEMA = {
|
||||
},
|
||||
htmlType: 'submit'
|
||||
},
|
||||
children: '提交'
|
||||
children: '提交',
|
||||
condition: true,
|
||||
loop: [1,2,3],
|
||||
conditionGroup: '1'
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
@ -138,7 +147,9 @@ const SCHEMA = {
|
||||
},
|
||||
htmlType: 'reset'
|
||||
},
|
||||
children: '重置'
|
||||
children: '重置',
|
||||
condition: false,
|
||||
conditionGroup: '1'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconBase, IconBaseProps } from "./icon-base";
|
||||
import { IconBase, IconProps } from "./icon-base";
|
||||
|
||||
export function IconClone(props: IconBaseProps) {
|
||||
export function IconClone(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M192 256.16C192 220.736 220.704 192 256.16 192h639.68C931.264 192 960 220.704 960 256.16v639.68A64.16 64.16 0 0 1 895.84 960H256.16A64.16 64.16 0 0 1 192 895.84V256.16z m64 31.584v576.512a32 32 0 0 0 31.744 31.744h576.512a32 32 0 0 0 31.744-31.744V287.744A32 32 0 0 0 864.256 256H287.744A32 32 0 0 0 256 287.744zM288 192v64h64V192H288z m128 0v64h64V192h-64z m128 0v64h64V192h-64z m128 0v64h64V192h-64z m128 0v64h64V192h-64z m96 96v64h64V288h-64z m0 128v64h64v-64h-64z m0 128v64h64v-64h-64z m0 128v64h64v-64h-64z m0 128v64h64v-64h-64z m-96 96v64h64v-64h-64z m-128 0v64h64v-64h-64z m-128 0v64h64v-64h-64z m-128 0v64h64v-64h-64z m-128 0v64h64v-64H288z m-96-96v64h64v-64H192z m0-128v64h64v-64H192z m0-128v64h64v-64H192z m0-128v64h64v-64H192z m0-128v64h64V288H192z m160 416c0-17.664 14.592-32 32.064-32h319.872a31.968 31.968 0 1 1 0 64h-319.872A31.968 31.968 0 0 1 352 704z m0-128c0-17.664 14.4-32 32.224-32h383.552c17.792 0 32.224 14.208 32.224 32 0 17.664-14.4 32-32.224 32H384.224A32.032 32.032 0 0 1 352 576z m0-128c0-17.664 14.4-32 32.224-32h383.552c17.792 0 32.224 14.208 32.224 32 0 17.664-14.4 32-32.224 32H384.224A32.032 32.032 0 0 1 352 448z m512 47.936V192h-64V159.968A31.776 31.776 0 0 0 768.032 128H160A31.776 31.776 0 0 0 128 159.968V768c0 17.92 14.304 31.968 31.968 31.968H192v64h303.936H128.128A63.968 63.968 0 0 1 64 799.872V128.128C64 92.704 92.48 64 128.128 64h671.744C835.296 64 864 92.48 864 128.128v367.808z"/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconBase, IconBaseProps } from "./icon-base";
|
||||
import { IconBase, IconProps } from "./icon-base";
|
||||
|
||||
export function IconComponent(props: IconBaseProps) {
|
||||
export function IconComponent(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M783.5648 437.4528h-18.0224V336.6912c0-43.8272-35.6352-79.4624-79.4624-79.4624h-110.592V241.664c0-90.9312-73.728-164.6592-164.6592-164.6592-90.9312 0-164.6592 73.728-164.6592 164.6592v15.5648H155.2384c-43.8272 0-79.4624 35.6352-79.4624 79.4624v131.4816c0 16.7936 13.9264 30.72 30.72 30.72h56.1152c56.9344 0 103.2192 46.2848 103.2192 103.2192s-46.2848 103.2192-103.2192 103.2192H106.496c-16.7936 0-30.72 13.9264-30.72 30.72v131.4816c0 43.8272 35.6352 79.4624 79.4624 79.4624h531.2512c43.8272 0 79.4624-35.6352 79.4624-79.4624v-100.7616h18.0224c90.9312 0 164.6592-73.728 164.6592-164.6592-0.4096-90.9312-74.1376-164.6592-165.0688-164.6592z m0 267.8784h-48.7424c-16.7936 0-30.72 13.9264-30.72 30.72v131.4816c0 9.8304-8.192 18.0224-18.0224 18.0224H155.2384c-9.8304 0-18.0224-8.192-18.0224-18.0224v-100.7616h25.3952c90.9312 0 164.6592-73.728 164.6592-164.6592 0-90.9312-73.728-164.6592-164.6592-164.6592h-25.3952V336.6912c0-9.8304 8.192-18.0224 18.0224-18.0224h121.6512c16.7936 0 30.72-13.9264 30.72-30.72V241.664c0-56.9344 46.2848-103.2192 103.2192-103.2192s103.2192 46.2848 103.2192 103.2192v46.2848c0 16.7936 13.9264 30.72 30.72 30.72h141.312c9.8304 0 18.0224 8.192 18.0224 18.0224v131.4816c0 16.7936 13.9264 30.72 30.72 30.72h48.7424c56.9344 0 103.2192 46.2848 103.2192 103.2192s-46.2848 103.2192-103.2192 103.2192z" />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconBase, IconBaseProps } from "./icon-base";
|
||||
import { IconBase, IconProps } from "./icon-base";
|
||||
|
||||
export function IconContainer(props: IconBaseProps) {
|
||||
export function IconContainer(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M800 800h64v64h-64v-64z m-128 0h64v64h-64v-64z m-128 0h64v64h-64v-64z m-128 0h64v64h-64v-64z m-256 0h64v64h-64v-64z m0-640h64v64h-64v-64z m128 640h64v64h-64v-64zM160 672h64v64h-64v-64z m0-128h64v64h-64v-64z m0-128h64v64h-64v-64z m0-128h64v64h-64v-64z m640 384h64v64h-64v-64z m0-128h64v64h-64v-64z m0-128h64v64h-64v-64z m0-128h64v64h-64v-64z m0-128h64v64h-64v-64z m-128 0h64v64h-64v-64z m-128 0h64v64h-64v-64z m-128 0h64v64h-64v-64z m-128 0h64v64h-64v-64z" />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconBase, IconBaseProps } from "./icon-base";
|
||||
import { IconBase, IconProps } from "./icon-base";
|
||||
|
||||
export function IconHidden(props: IconBaseProps) {
|
||||
export function IconHidden(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M183.423543 657.078213l163.499771-98.484012c-4.233418-14.908548-6.646374-30.585599-6.646374-46.852074 0-94.665033 76.739778-171.404812 171.404812-171.404812 45.983287 0 87.641059 18.20871 118.42518 47.679929l129.791042-78.17957c-73.254398-41.73145-157.866471-65.812915-248.216221-65.812915-192.742792 0-360.068705 108.505249-444.453604 267.715321C96.636944 567.228859 136.301316 616.355743 183.423543 657.078213zM841.253886 367.552144l-164.382884 99.015108c3.934612 14.415314 6.215562 29.513174 6.215562 45.174875 0 94.665033-76.739778 171.404812-171.404812 171.404812-45.361117 0-86.484723-17.747199-117.142977-46.515407l-129.419582 77.955466c72.874751 41.149189 156.893306 64.871473 246.563582 64.871473 192.742792 0 360.068705-108.505249 444.453604-267.717368C927.000805 456.773188 887.794875 408.054603 841.253886 367.552144zM420.280042 511.741104c0 0.550539 0.152473 1.060145 0.161682 1.608637l135.080511-81.366146c-13.065574-7.198959-27.854395-11.658528-43.826158-11.658528C461.20922 420.325068 420.280042 461.254246 420.280042 511.741104zM447.739441 576.947198l69.02098-41.574884L948.364369 275.395234c10.812253-6.512321 14.297634-20.558222 7.785314-31.369452-6.512321-10.812253-20.556175-14.296611-31.368428-7.785314L575.654762 446.537056l0 0-151.20577 91.078345 0 0L75.027787 748.090043c-10.812253 6.512321-14.297634 20.556175-7.785314 31.368428 6.512321 10.812253 20.556175 14.297634 31.369452 7.785314L447.739441 576.947198 447.739441 576.947198zM511.696078 603.157139c50.487881 0 91.416036-40.928155 91.416036-91.416036 0-0.549515-0.152473-1.057075-0.161682-1.605567l-135.079488 81.364099C480.935494 598.699618 495.724315 603.157139 511.696078 603.157139z" />
|
||||
|
||||
@ -8,16 +8,24 @@ const SizePresets: any = {
|
||||
xlarge: 30,
|
||||
};
|
||||
|
||||
export interface IconBaseProps {
|
||||
export interface IconProps {
|
||||
className?: string;
|
||||
fill?: string;
|
||||
size?: 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | number;
|
||||
viewBox: string;
|
||||
children?: ReactNode;
|
||||
style?: object;
|
||||
};
|
||||
}
|
||||
|
||||
export function IconBase({ fill, size = 'medium', viewBox, style, children, ...props }: IconBaseProps) {
|
||||
export function IconBase({
|
||||
fill,
|
||||
size = 'medium',
|
||||
viewBox,
|
||||
style,
|
||||
children,
|
||||
...props
|
||||
}: IconProps & {
|
||||
viewBox: string;
|
||||
}) {
|
||||
if (SizePresets.hasOwnProperty(size)) {
|
||||
size = SizePresets[size];
|
||||
}
|
||||
@ -31,10 +39,11 @@ export function IconBase({ fill, size = 'medium', viewBox, style, children, ...p
|
||||
viewBox={viewBox}
|
||||
{...props}
|
||||
style={{
|
||||
verticalAlign: 'middle',
|
||||
color: fill,
|
||||
...style,
|
||||
}}
|
||||
>{children}</svg>
|
||||
>
|
||||
{children}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconBase, IconBaseProps } from "./icon-base";
|
||||
import { IconBase, IconProps } from "./icon-base";
|
||||
|
||||
export function IconPage(props: IconBaseProps) {
|
||||
export function IconPage(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M381.6 864H162.4c-6.9 0-12.4 4.6-12.4 10.3v19.3c0 5.7 5.6 10.3 12.4 10.3h219.1c6.8 0 12.4-4.6 12.4-10.3v-19.3c0.1-5.7-5.5-10.3-12.3-10.3zM382 780.6H162c-6.9 0-12.5 4.6-12.5 10.3v19.3c0 5.7 5.6 10.3 12.5 10.3h220c6.9 0 12.5-4.6 12.5-10.3v-19.3c0-5.7-5.6-10.3-12.5-10.3zM162.4 737.2h219.1c6.8 0 12.4-4.6 12.4-10.3v-19.3c0-5.7-5.6-10.3-12.4-10.3H162.4c-6.9 0-12.4 4.6-12.4 10.3v19.3c0 5.7 5.6 10.3 12.4 10.3z" />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconBase, IconBaseProps } from './icon-base';
|
||||
import { IconBase, IconProps } from './icon-base';
|
||||
|
||||
export function IconRemove(props: IconBaseProps) {
|
||||
export function IconRemove(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M224 256v639.84A64 64 0 0 0 287.84 960h448.32A64 64 0 0 0 800 895.84V256h64a32 32 0 1 0 0-64H160a32 32 0 1 0 0 64h64zM384 96c0-17.664 14.496-32 31.904-32h192.192C625.696 64 640 78.208 640 96c0 17.664-14.496 32-31.904 32H415.904A31.872 31.872 0 0 1 384 96z m-96 191.744C288 270.208 302.4 256 320.224 256h383.552C721.6 256 736 270.56 736 287.744v576.512C736 881.792 721.6 896 703.776 896H320.224A32.224 32.224 0 0 1 288 864.256V287.744zM352 352c0-17.696 14.208-32.032 32-32.032 17.664 0 32 14.24 32 32v448c0 17.664-14.208 32-32 32-17.664 0-32-14.24-32-32V352z m128 0c0-17.696 14.208-32.032 32-32.032 17.664 0 32 14.24 32 32v448c0 17.664-14.208 32-32 32-17.664 0-32-14.24-32-32V352z m128 0c0-17.696 14.208-32.032 32-32.032 17.664 0 32 14.24 32 32v448c0 17.664-14.208 32-32 32-17.664 0-32-14.24-32-32V352z" />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconBase, IconBaseProps } from './icon-base';
|
||||
import { IconBase, IconProps } from './icon-base';
|
||||
|
||||
export function IconSetting(props: IconBaseProps) {
|
||||
export function IconSetting(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M965.824 405.952a180.48 180.48 0 0 1-117.12-85.376 174.464 174.464 0 0 1-16-142.08 22.208 22.208 0 0 0-7.04-23.552 480.576 480.576 0 0 0-153.6-89.216 23.104 23.104 0 0 0-24.32 5.76 182.208 182.208 0 0 1-135.68 57.92 182.208 182.208 0 0 1-133.12-56.64 23.104 23.104 0 0 0-26.88-7.04 478.656 478.656 0 0 0-153.6 89.856 22.208 22.208 0 0 0-7.04 23.552 174.464 174.464 0 0 1-16 141.44A180.48 180.48 0 0 1 58.24 405.952a22.4 22.4 0 0 0-17.28 17.792 455.08 455.08 0 0 0 0 176.512 22.4 22.4 0 0 0 17.28 17.792 180.48 180.48 0 0 1 117.12 84.736c25.408 42.944 31.232 94.592 16 142.08a22.208 22.208 0 0 0 7.04 23.552A480.576 480.576 0 0 0 352 957.632h7.68a23.04 23.04 0 0 0 16.64-7.04 184.128 184.128 0 0 1 266.944 0c6.592 8.96 18.752 11.968 28.8 7.04a479.36 479.36 0 0 0 156.16-88.576 22.208 22.208 0 0 0 7.04-23.552 174.464 174.464 0 0 1 13.44-142.72 180.48 180.48 0 0 1 117.12-84.736 22.4 22.4 0 0 0 17.28-17.792 452.613 452.613 0 0 0 0-176.512 23.04 23.04 0 0 0-17.28-17.792z m-42.88 169.408a218.752 218.752 0 0 0-128 98.112 211.904 211.904 0 0 0-21.76 156.736 415.936 415.936 0 0 1-112 63.68 217.472 217.472 0 0 0-149.12-63.68 221.312 221.312 0 0 0-149.12 63.68 414.592 414.592 0 0 1-112-63.68c12.8-53.12 4.288-109.12-23.68-156.096A218.752 218.752 0 0 0 101.12 575.36a386.176 386.176 0 0 1 0-127.36 218.752 218.752 0 0 0 128-98.112c27.2-47.552 34.944-103.68 21.76-156.8a415.296 415.296 0 0 1 112-63.68A221.44 221.44 0 0 0 512 187.392a218.24 218.24 0 0 0 149.12-57.984 413.952 413.952 0 0 1 112 63.744 211.904 211.904 0 0 0 23.04 156.096 218.752 218.752 0 0 0 128 98.112 386.65 386.65 0 0 1 0 127.36l-1.28 0.64z" />
|
||||
|
||||
@ -62,6 +62,9 @@ class Intl extends PureComponent<{ data: any; params?: object }> {
|
||||
|
||||
export function intl(data: any, params?: object): ReactNode {
|
||||
if (isI18nData(data)) {
|
||||
if (data.intl) {
|
||||
return data.intl;
|
||||
}
|
||||
return <Intl data={data} params={params} />;
|
||||
}
|
||||
return data;
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface I18nData {
|
||||
type: 'i18n';
|
||||
[key: string]: string;
|
||||
intl?: ReactNode;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// type checks
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
import { INode, isElementNode, isRootNode } from '../../../../document/node';
|
||||
|
||||
export function isContainer(node: INode): boolean {
|
||||
if (isRootNode(node)) {
|
||||
return true;
|
||||
}
|
||||
if (isElementNode(node)) {
|
||||
// TODO: check from prototype
|
||||
// block Fragment
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562677537258" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="907" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M965.46812628 219.26174551H58.53187372c-27.20256421 0-42.39181327 28.72148912-25.54555523 48.3294288l453.46812628 525.82418542c12.97990373 15.05116498 37.97312263 15.05116498 51.09111046 0L991.01368151 267.59117431c16.84625804-19.6079397 1.65700898-48.3294288-25.54555523-48.3294288z" fill="#ffffff" p-id="908"></path></svg>
|
||||
|
Before Width: | Height: | Size: 700 B |
@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562677542938" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1019" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M793.41535973 486.45444477L267.59117431 32.98631849c-19.6079397-16.84625804-48.3294288-1.65700898-48.3294288 25.54555523v906.93625256c0 27.20256421 28.72148912 42.39181327 48.3294288 25.54555523l525.82418542-453.46812628c15.05116498-12.97990373 15.05116498-38.11120672 0-51.09111046z" fill="#ffffff" p-id="1020"></path></svg>
|
||||
|
Before Width: | Height: | Size: 702 B |
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Designer not found": "Designer not found",
|
||||
"No opened document": "No opened document",
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Designer not found": "未发现设计器模块",
|
||||
"No opened document": "没有打开的文档"
|
||||
}
|
||||
@ -1,251 +0,0 @@
|
||||
/* 面板背景的颜色 */
|
||||
@pane-bgcolor: #333131; // #1a1c23;
|
||||
/* 标题背景色 */
|
||||
//@title-bgcolor: var(--pane-bg-color; // backup rgba(0, 0, 0, 0.2);
|
||||
/* 标题边框色 */
|
||||
@title-bdcolor: transparent;
|
||||
@title-selectedcolor: #111;
|
||||
@section-bgcolor: transparent;
|
||||
@section-bdcolor: rgba(0, 0, 0, 0.1);
|
||||
/* 文字颜色 */
|
||||
@text-color: #ffffff;
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.my-outline-pane {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
||||
> .tree-scroll-container {
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.my-outline-tree {
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
padding-left: 5px;
|
||||
|
||||
// 禁用 Text Select
|
||||
user-select: none;
|
||||
|
||||
.insertion {
|
||||
pointer-events: none !important;
|
||||
border: 1px dashed var(--color-brand-light);
|
||||
height: 25px;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.condition-flow-container {
|
||||
@bd-setting: 1px solid #7b605b;
|
||||
border-top: @bd-setting;
|
||||
border-bottom: @bd-setting;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: @bd-setting;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
color: rgb(217, 217, 217);
|
||||
|
||||
.c-control-flow-title {
|
||||
text-align: center;
|
||||
background-color: #7b605b;
|
||||
height: 14px;
|
||||
> b {
|
||||
transform: scale(0.75);
|
||||
transform-origin: top;
|
||||
text-shadow: 0px 0px 2px black;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-collapsed-icon {
|
||||
transition: transform 0.01s;
|
||||
margin-left: 4px;
|
||||
filter: opacity(0.5);
|
||||
|
||||
& > svg {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: opacity(1);
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-icon {
|
||||
transform: translateZ(0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
|
||||
& > svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-ignored-icon {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
.tree-node-title {
|
||||
font-size: var(--font-size-text);
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
background: var(--pane-bg-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
transform: translateZ(0);
|
||||
|
||||
.tree-node-title-inner {
|
||||
flex: 1;
|
||||
height: 26px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tree-node-title-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background-color: var(--pane-bg-color);
|
||||
color: var(--color-pane-label);
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.tree-node-title-text {
|
||||
flex: 1;
|
||||
color: rgb(217, 217, 217);
|
||||
// problem
|
||||
//width: 100%;
|
||||
//height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.x-for-text {
|
||||
color: #9370db;
|
||||
}
|
||||
|
||||
&.x-if-text {
|
||||
color: #ff6308;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.editable {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
> .tree-node-title > .tree-node-collapsed-icon {
|
||||
transform-origin: center;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
//> .branches {
|
||||
// > .tree-node > .tree-node-title > .tree-node-icon {
|
||||
// margin-left: 9px;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
&.hover {
|
||||
& > .tree-node-title {
|
||||
background: @title-selectedcolor * 1.6;
|
||||
|
||||
.tree-node-ignored-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 忽略节点处理
|
||||
&.ignored {
|
||||
.tree-node-title-text {
|
||||
color: #9b9b9b;
|
||||
}
|
||||
|
||||
.tree-node-collapsed-icon,
|
||||
.tree-node-ignored-icon {
|
||||
display: block;
|
||||
filter: opacity(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// 选中节点处理
|
||||
&.selected {
|
||||
& > .tree-node-title {
|
||||
background: @title-selectedcolor;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理拖入节点
|
||||
&.dropping {
|
||||
& > .tree-node-title {
|
||||
background: @title-selectedcolor * 0.75;
|
||||
}
|
||||
|
||||
& > .branches:before {
|
||||
border-left: 1px solid var(--color-brand-light);
|
||||
}
|
||||
}
|
||||
|
||||
.branches {
|
||||
padding-left: 12px;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: 0.5px solid rgba(149, 216, 160, 0.25);
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 10px;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.x-flow {
|
||||
&:before {
|
||||
border-left: 1px solid #ff6308;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
import { observer } from '../../../globals/src';
|
||||
|
||||
@observer
|
||||
export default class TreeBranches extends Component<TreeNodeProps> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const treeNode = this.props.treeNode;
|
||||
const { expanded } = treeNode;
|
||||
|
||||
if (!expanded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const branchClassName = classNames({
|
||||
branches: !isRootNode(treeNode.node),
|
||||
// 'x-branch': treeNode.hasXIf() && treeNode.branchIndex !== treeNode.branchNode!.children.length - 1,
|
||||
});
|
||||
|
||||
let children: any = [];
|
||||
|
||||
if (treeNode.hasChildren() /* || node.hasSlots() */) {
|
||||
children = treeNode.children.map((child: TreeNode) => {
|
||||
if (child.hasXIf()) {
|
||||
if (child.flowIndex === 0) {
|
||||
const conditionFlowContainer = classNames('condition-group-container', {
|
||||
hidden: child.hidden,
|
||||
});
|
||||
return (
|
||||
<div key={child.id} className={conditionFlowContainer} data-id={child.id}>
|
||||
<div className="c-control-flow-title"><b>Condition Flow</b></div>
|
||||
{child.conditionGroup!.children.map(c => {
|
||||
return <TreeNodeView key={c.id} treeNode={tree.getTreeNode(c)} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return <TreeNodeView key={child.id} treeNode={child} />;
|
||||
});
|
||||
}
|
||||
if (treeNode.dropIndex != null) {
|
||||
children.splice(
|
||||
treeNode.dropIndex,
|
||||
0,
|
||||
<div key="insertion" ref={ref => tree.mountInsertion(ref)} className="insertion" />,
|
||||
);
|
||||
}
|
||||
|
||||
return children.length > 0 && <div className={branchClassName}>{children}</div>;
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
import React from 'react';
|
||||
import DIVIcon from 'my-icons/container.svg';
|
||||
import IMGIcon from 'my-icons/image.svg';
|
||||
import { observer } from '@ali/recore';
|
||||
|
||||
@observer
|
||||
export default class TreeNodeIconView extends React.Component<{ tagName: string }> {
|
||||
shouldComponentUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tagName } = this.props;
|
||||
switch (tagName) {
|
||||
case 'img': {
|
||||
console.log('>>> tag:', tagName);
|
||||
return <IMGIcon />;
|
||||
}
|
||||
default:
|
||||
return <DIVIcon />;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
|
||||
|
||||
export interface TreeNodeProps {
|
||||
treeNode: TreeNode;
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class TreeNodeView extends Component<TreeNodeProps> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const className = classNames('tree-node', {
|
||||
// 是否展开
|
||||
expanded: treeNode.expanded,
|
||||
// 是否悬停
|
||||
hover: treeNode.hover,
|
||||
// 是否选中
|
||||
selected: treeNode.selected,
|
||||
// 是否隐藏
|
||||
hidden: treeNode.hidden,
|
||||
// 是否忽略的
|
||||
ignored: treeNode.ignored,
|
||||
// 是否锁定的
|
||||
locked: treeNode.locked,
|
||||
// 是否投放响应
|
||||
dropping: treeNode.dropIndex != null,
|
||||
// 是否?
|
||||
highlight: treeNode.isDropContainer() && treeNode.dropIndex == null,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className} data-id={treeNode.id}>
|
||||
<TreeTitle treeNode={treeNode} />
|
||||
<TreeBranches treeNode={treeNode} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function findTargetByEvent(e: MouseEvent): HTMLElement | null {
|
||||
return (e.target as HTMLElement).closest('.tree-node') as HTMLElement;
|
||||
}
|
||||
|
||||
export function getNodeIDFromTarget(target: HTMLElement): string | null {
|
||||
return target.getAttribute('data-id');
|
||||
}
|
||||
|
||||
export function getNodeIDByEvent(e: MouseEvent): string | null {
|
||||
const target = findTargetByEvent(e);
|
||||
if (target) {
|
||||
return getNodeIDFromTarget(target);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -1,176 +0,0 @@
|
||||
import { observer } from '@ali/recore';
|
||||
import React, { Component, KeyboardEvent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import ElementNode from '../../../../document/node/element-node';
|
||||
import { isElementNode } from '../../../../document/node';
|
||||
import { TreeNodeProps } from './tree-node';
|
||||
import TreeNodeIconView from './tree-node-icon-view';
|
||||
import CollapsedIcon from '../icons/caret-right.svg';
|
||||
import EyeCloseIcon from 'my-icons/eye-close.svg';
|
||||
|
||||
interface IState {
|
||||
editing: boolean;
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class TreeNodeTitle extends Component<TreeNodeProps, IState> {
|
||||
|
||||
private inputRef = React.createRef<HTMLInputElement>();
|
||||
|
||||
constructor(props: TreeNodeProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
|
||||
toggleIgnored() {
|
||||
const treeNode = this.props.treeNode;
|
||||
const node = treeNode.node as ElementNode;
|
||||
if (treeNode.ignored) {
|
||||
node.getDirective('x-ignore').remove();
|
||||
} else {
|
||||
node.getDirective('x-ignore').value = true;
|
||||
}
|
||||
}
|
||||
|
||||
toggleExpanded() {
|
||||
const treeNode = this.props.treeNode;
|
||||
const { expanded } = treeNode;
|
||||
treeNode.expanded = !expanded;
|
||||
}
|
||||
|
||||
renderExpandIcon() {
|
||||
const node = this.props.treeNode;
|
||||
|
||||
if (!node.expandable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="tree-node-collapsed-icon"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
this.toggleExpanded();
|
||||
}}
|
||||
>
|
||||
<CollapsedIcon />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
setTitle(xtitle: string = '') {
|
||||
const { treeNode } = this.props;
|
||||
const node = treeNode.node as ElementNode;
|
||||
const title = node.getProp('x-title');
|
||||
if (xtitle && xtitle !== node.tagName) {
|
||||
title.code = `"${xtitle}"`;
|
||||
} else {
|
||||
title.remove();
|
||||
}
|
||||
}
|
||||
|
||||
enableEdit = () => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
});
|
||||
}
|
||||
|
||||
cancelEdit() {
|
||||
this.setState({
|
||||
editing: false,
|
||||
});
|
||||
}
|
||||
|
||||
saveEdit = () => {
|
||||
const { current } = this.inputRef;
|
||||
|
||||
if (current) {
|
||||
this.setTitle(current.value);
|
||||
}
|
||||
|
||||
this.cancelEdit();
|
||||
}
|
||||
|
||||
handleKeyUp(e: KeyboardEvent<HTMLInputElement>) {
|
||||
if (e.keyCode === 13) {
|
||||
this.saveEdit();
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
this.cancelEdit();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { current } = this.inputRef;
|
||||
if (current) {
|
||||
current.select();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const { editing } = this.state;
|
||||
const { title } = treeNode;
|
||||
const depth = treeNode.depth;
|
||||
const indent = depth * 12;
|
||||
|
||||
const titleClassName = classNames('tree-node-title');
|
||||
const titleTextClassName = classNames('tree-node-title-text', {
|
||||
'x-if-text': treeNode.hasXIf(),
|
||||
'x-for-text': treeNode.hasXFor(),
|
||||
});
|
||||
const xForValue = treeNode.xForValue;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={titleClassName}
|
||||
ref={ref => treeNode.mount(ref)}
|
||||
style={{ paddingLeft: indent, marginLeft: -indent }}
|
||||
>
|
||||
{this.renderExpandIcon()}
|
||||
<div className="tree-node-icon">
|
||||
<TreeNodeIconView tagName={treeNode.node.tagName} />
|
||||
</div>
|
||||
<div className="tree-node-title-inner" onDoubleClick={this.enableEdit}>
|
||||
{
|
||||
editing ?
|
||||
<input
|
||||
className="tree-node-title-input"
|
||||
defaultValue={title.label}
|
||||
onBlur={this.saveEdit}
|
||||
onKeyUp={e => {this.handleKeyUp(e)}}
|
||||
ref={this.inputRef}
|
||||
/>
|
||||
:
|
||||
<div className={titleTextClassName}>
|
||||
{title.label}
|
||||
{xForValue && (
|
||||
<span className="info">
|
||||
(x <b>{xForValue.length}</b>)
|
||||
</span>
|
||||
)}
|
||||
{treeNode.hasXIf() && (
|
||||
<span className="info">
|
||||
<b>{treeNode.flowHidden ? '' : '(visible)'}</b>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div className="tree-node-ignored-icon">
|
||||
{isElementNode(treeNode.node) && !editing && (
|
||||
<EyeCloseIcon
|
||||
onClick={(e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
this.toggleIgnored();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@ali/lowcode-plugin-outline-pane",
|
||||
"name": "@ali/lowcode-plugin-outline-tree",
|
||||
"version": "0.0.0",
|
||||
"description": "xxx for Ali lowCode engine",
|
||||
"main": "src/index.ts",
|
||||
11
packages/plugin-outline-tree/src/icons/arrow-right.tsx
Normal file
11
packages/plugin-outline-tree/src/icons/arrow-right.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { IconBase, IconProps } from '../../../globals';
|
||||
|
||||
export function IconArrowRight(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512.002047 771.904425c-10.152221 0.518816-20.442588-2.800789-28.202319-10.598382L77.902254 315.937602c-14.548344-14.618952-14.548344-38.318724 0-52.933583 14.544251-14.614859 38.118156-14.614859 52.662407 0l381.437385 418.531212L893.432269 263.004019c14.544251-14.614859 38.125319-14.614859 52.662407 0 14.552437 14.614859 14.552437 38.314631 0 52.933583L540.205389 761.307066C532.451798 769.103636 522.158361 772.424264 512.002047 771.904425z"/>
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconArrowRight.displayName = 'IconArrowRight';
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
11
packages/plugin-outline-tree/src/icons/cond.tsx
Normal file
11
packages/plugin-outline-tree/src/icons/cond.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { IconBase, IconProps } from '../../../globals';
|
||||
|
||||
export function IconCond(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M479.552 276.544l296.896 2.752v75.712L960 249.024l-183.552-106.048v92.48h-271.36l-46.656-2.752-190.784 203.648 30.976 30.976 180.928-190.784z m296.896 484.928l-253.056-2.816-262.976-263.04H64v43.904h175.296l262.912 262.976 274.176 2.816v75.712L960 774.976l-183.616-105.984 0.064 92.48z" />
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconCond.displayName = 'IconCond';
|
||||
12
packages/plugin-outline-tree/src/icons/eye-close.tsx
Normal file
12
packages/plugin-outline-tree/src/icons/eye-close.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { IconBase, IconProps } from '../../../globals';
|
||||
|
||||
export function IconEyeClose(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512.7 700.9c-102.1 0-184.9-82.8-184.9-184.9 0-28.6 6.5-55.6 18-79.7l-93.7-93.7C138.9 418.1 65.2 514 65.2 514s200.4 260.7 447.6 260.7c50.2 0 98.6-10.8 143.6-27.9l-63.9-63.9c-24.2 11.5-51.2 18-79.8 18z" />
|
||||
<path d="M960.3 514S759.9 253.3 512.7 253.3c-49.5 0-97.2 10.5-141.7 27.2L243.5 153.1l-45.3 45.3 262.3 262.2c-13.1 13.3-21.2 31.5-21.2 51.6 0 40.6 32.9 73.4 73.4 73.4 20.1 0 38.4-8.1 51.6-21.2l260.9 260.8 45.3-45.3-95.6-95.6C887.2 609.1 960.3 514 960.3 514z m-376.7-20.9c-6.8-25.2-26.6-45.1-51.9-51.9L437.5 347c23-10.3 48.5-16 75.3-16 102.1 0 184.9 82.8 184.9 184.9 0 26.8-5.7 52.2-15.9 75.2l-98.2-98z" />
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconEyeClose.displayName = 'IconEyeClose';
|
||||
12
packages/plugin-outline-tree/src/icons/eye.tsx
Normal file
12
packages/plugin-outline-tree/src/icons/eye.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { IconBase, IconProps } from '../../../globals';
|
||||
|
||||
export function IconEye(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M512 256c-163.8 0-291.4 97.6-448 256 134.8 135.4 248 256 448 256 199.8 0 346.8-152.8 448-253.2C856.4 397.2 709.6 256 512 256z m0 438.6c-98.8 0-179.2-82-179.2-182.6 0-100.8 80.4-182.6 179.2-182.6s179.2 82 179.2 182.6c0 100.8-80.4 182.6-179.2 182.6z" />
|
||||
<path d="M512 448c0-15.8 5.8-30.2 15.2-41.4-5-0.8-10-1.2-15.2-1.2-57.6 0-104.6 47.8-104.6 106.6s47 106.6 104.6 106.6 104.6-47.8 104.6-106.6c0-4.6-0.4-9.2-0.8-13.8-11 8.6-24.6 13.8-39.6 13.8-35.6 0-64.2-28.6-64.2-64z" />
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconEye.displayName = 'IconEye';
|
||||
11
packages/plugin-outline-tree/src/icons/lock.tsx
Normal file
11
packages/plugin-outline-tree/src/icons/lock.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { IconBase, IconProps } from '../../../globals';
|
||||
|
||||
export function IconLock(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M832 464h-68V240c0-70.7-57.3-128-128-128H388c-70.7 0-128 57.3-128 128v224h-68c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32zM540 701v53c0 4.4-3.6 8-8 8h-40c-4.4 0-8-3.6-8-8v-53c-12.1-8.7-20-22.9-20-39 0-26.5 21.5-48 48-48s48 21.5 48 48c0 16.1-7.9 30.3-20 39z m152-237H332V240c0-30.9 25.1-56 56-56h248c30.9 0 56 25.1 56 56v224z" />
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconLock.displayName = 'IconLock';
|
||||
11
packages/plugin-outline-tree/src/icons/loop.tsx
Normal file
11
packages/plugin-outline-tree/src/icons/loop.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { IconBase, IconProps } from '../../../globals';
|
||||
|
||||
export function IconLoop(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M60.235294 542.117647c0 132.879059 103.062588 240.941176 229.677176 240.941176l0 60.235294c-159.864471 0-289.912471-135.107765-289.912471-301.176471s130.048-301.176471 289.912471-301.176471l254.735059 0-99.147294-99.147294 42.586353-42.586353 171.911529 171.851294-171.851294 171.911529-42.646588-42.646588 99.207529-99.147294-254.795294 0c-126.614588 0-229.677176 108.062118-229.677176 240.941176zM734.087529 240.941176l0 60.235294c126.614588 0 229.677176 108.062118 229.677176 240.941176s-103.062588 240.941176-229.677176 240.941176l-254.795294 0 99.147294-99.147294-42.586353-42.586353-171.851294 171.851294 171.911529 171.911529 42.586353-42.586353-99.207529-99.207529 254.735059 0c159.924706 0 289.972706-135.107765 289.972706-301.176471s-130.048-301.176471-289.912471-301.176471z" />
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconLoop.displayName = 'IconLoop';
|
||||
12
packages/plugin-outline-tree/src/icons/slot.tsx
Normal file
12
packages/plugin-outline-tree/src/icons/slot.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { IconBase, IconProps } from '../../../globals/src';
|
||||
|
||||
export function IconSlot(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M682.325333 135.509333V204.8H819.2v613.376h-614.741333V204.8h136.874666v-69.290667h-206.165333v752.298667h754.346667V135.509333z" />
|
||||
<path d="M512 512m-170.325333 0a170.325333 170.325333 0 1 0 340.650666 0 170.325333 170.325333 0 1 0-340.650666 0Z" />
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconSlot.displayName = 'IconSlot';
|
||||
12
packages/plugin-outline-tree/src/icons/unlock.tsx
Normal file
12
packages/plugin-outline-tree/src/icons/unlock.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { IconBase, IconProps } from '../../../globals/src';
|
||||
|
||||
export function IconUnlock(props: IconProps) {
|
||||
return (
|
||||
<IconBase viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M832 464H332V240c0-30.9 25.1-56 56-56h248c30.9 0 56 25.1 56 56v68c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-68c0-70.7-57.3-128-128-128H388c-70.7 0-128 57.3-128 128v224h-68c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32z m-40 376H232V536h560v304z" />
|
||||
<path d="M484 701v53c0 4.4 3.6 8 8 8h40c4.4 0 8-3.6 8-8v-53c12.1-8.7 20-22.9 20-39 0-26.5-21.5-48-48-48s-48 21.5-48 48c0 16.1 7.9 30.3 20 39z" />
|
||||
</IconBase>
|
||||
);
|
||||
}
|
||||
|
||||
IconUnlock.displayName = 'IconUnlock';
|
||||
@ -1,5 +1,6 @@
|
||||
import Pane from './views/pane';
|
||||
|
||||
/*
|
||||
export default {
|
||||
name: 'outline-tree',
|
||||
title: {
|
||||
@ -8,3 +9,6 @@ export default {
|
||||
},
|
||||
content: Pane,
|
||||
};
|
||||
*/
|
||||
|
||||
export default Pane;
|
||||
14
packages/plugin-outline-tree/src/locale/en-US.json
Normal file
14
packages/plugin-outline-tree/src/locale/en-US.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Designer not found": "Designer not found",
|
||||
"No opened document": "No opened document",
|
||||
"Hide": "Hide",
|
||||
"Show": "Show",
|
||||
"Lock": "Lock",
|
||||
"Unlock": "Unlock",
|
||||
"Expand": "Expand",
|
||||
"Collapse": "Collapse",
|
||||
"Conditional": "Condition",
|
||||
"Loop": "Loop",
|
||||
"Slots": "Slots",
|
||||
"Slot for {prop}": "Slot for {prop}"
|
||||
}
|
||||
14
packages/plugin-outline-tree/src/locale/zh-CN.json
Normal file
14
packages/plugin-outline-tree/src/locale/zh-CN.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Designer not found": "未发现设计器模块",
|
||||
"No opened document": "没有打开的文档",
|
||||
"Hide": "隐藏",
|
||||
"Show": "显示",
|
||||
"Lock": "锁定",
|
||||
"Unlock": "解锁",
|
||||
"Expand": "展开",
|
||||
"Collapse": "收起",
|
||||
"Conditional": "条件式",
|
||||
"Loop": "循环",
|
||||
"Slots": "插槽",
|
||||
"Slot for {prop}": "属性 {prop} 的插槽"
|
||||
}
|
||||
@ -5,7 +5,22 @@ import { Tree } from './tree';
|
||||
import Location from '../../designer/src/designer/helper/location';
|
||||
|
||||
class TreeMaster {
|
||||
constructor(readonly designer: Designer) {}
|
||||
constructor(readonly designer: Designer) {
|
||||
designer.dragon.onDragstart((e) => {
|
||||
const tree = this.currentTree;
|
||||
if (tree) {
|
||||
tree.document.selection.getTopNodes().forEach(node => {
|
||||
tree.getTreeNode(node).setExpanded(false);
|
||||
});
|
||||
};
|
||||
});
|
||||
designer.activeTracker.onChange((target) => {
|
||||
const tree = this.currentTree;
|
||||
if (tree && target.node.document === tree.document) {
|
||||
tree.getTreeNode(target.node).expandParents();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private treeMap = new Map<string, Tree>();
|
||||
@computed get currentTree(): Tree | null {
|
||||
@ -70,7 +85,7 @@ export class OutlineMain implements ISensor {
|
||||
private setupDesigner(designer: Designer) {
|
||||
this._designer = designer;
|
||||
this._master = getTreeMaster(designer);
|
||||
designer.dragon.addSensor(this);
|
||||
// designer.dragon.addSensor(this);
|
||||
}
|
||||
|
||||
purge() {
|
||||
@ -93,7 +108,7 @@ export class OutlineMain implements ISensor {
|
||||
}
|
||||
this._shell = shell;
|
||||
if (shell) {
|
||||
this._sensorAvailable = true;
|
||||
// this._sensorAvailable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,10 @@
|
||||
import { computed, obx, TitleContent } from '../../globals';
|
||||
import { computed, obx, TitleContent, isI18nData, localeFormat } from '../../globals';
|
||||
import Node from '../../designer/src/designer/document/node/node';
|
||||
import DocumentModel from '../../designer/src/designer/document/document-model';
|
||||
import { isLocationChildrenDetail } from '../../designer/src/designer/helper/location';
|
||||
import Designer from '../../designer/src/designer/designer';
|
||||
import { Tree } from './tree';
|
||||
|
||||
export interface Title {
|
||||
label: string;
|
||||
icon?: string;
|
||||
actions?: any;
|
||||
}
|
||||
|
||||
export default class TreeNode {
|
||||
get id(): string {
|
||||
return this.node.id;
|
||||
@ -35,6 +29,10 @@ export default class TreeNode {
|
||||
return this.node.zLevel;
|
||||
}
|
||||
|
||||
isRoot() {
|
||||
return this.tree.root === this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是响应投放区
|
||||
*/
|
||||
@ -52,10 +50,10 @@ export default class TreeNode {
|
||||
*/
|
||||
@obx.ref private _expanded = false;
|
||||
get expanded(): boolean {
|
||||
return this.expandable && this._expanded;
|
||||
return this.isRoot() || (this.expandable && this._expanded);
|
||||
}
|
||||
|
||||
set expanded(value: boolean) {
|
||||
setExpanded(value: boolean) {
|
||||
this._expanded = value;
|
||||
}
|
||||
|
||||
@ -64,17 +62,36 @@ export default class TreeNode {
|
||||
}
|
||||
|
||||
@computed get hidden(): boolean {
|
||||
const cv = this.node.isConditionalVisible();
|
||||
if (cv == null) {
|
||||
return this.node.getExtraProp('hidden', false)?.getValue() === true;
|
||||
}
|
||||
return !cv;
|
||||
}
|
||||
|
||||
@computed get ignored(): boolean {
|
||||
return this.node.getExtraProp('ignored', false)?.getValue() === true;
|
||||
setHidden(flag: boolean) {
|
||||
if (this.node.conditionGroup) {
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
this.node.getExtraProp('hidden', true)?.setValue(true);
|
||||
} else {
|
||||
this.node.getExtraProp('hidden', false)?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@computed get locked(): boolean {
|
||||
return this.node.getExtraProp('locked', false)?.getValue() === true;
|
||||
}
|
||||
|
||||
setLocked(flag: boolean) {
|
||||
if (flag) {
|
||||
this.node.getExtraProp('locked', true)?.setValue(true);
|
||||
} else {
|
||||
this.node.getExtraProp('locked', false)?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@computed get selected(): boolean {
|
||||
// TODO: check is dragging
|
||||
const selection = this.document.selection;
|
||||
@ -85,6 +102,39 @@ export default class TreeNode {
|
||||
return this.node.title;
|
||||
}
|
||||
|
||||
@computed get titleLabel() {
|
||||
let title = this.title;
|
||||
if (!title) {
|
||||
return '';
|
||||
}
|
||||
if ((title as any).label) {
|
||||
title = (title as any).label;
|
||||
}
|
||||
if (typeof title === 'string') {
|
||||
return title;
|
||||
}
|
||||
if (isI18nData(title)) {
|
||||
return localeFormat(title);
|
||||
}
|
||||
return this.node.componentName;
|
||||
}
|
||||
|
||||
setTitleLabel(label: string) {
|
||||
const origLabel = this.titleLabel;
|
||||
if (label === origLabel) {
|
||||
return;
|
||||
}
|
||||
if (label === '') {
|
||||
this.node.getExtraProp('title', false)?.remove();
|
||||
} else {
|
||||
this.node.getExtraProp('title', true)?.setValue(label);
|
||||
}
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.node.componentMeta.icon;
|
||||
}
|
||||
|
||||
@computed get parent() {
|
||||
const parent = this.node.parent;
|
||||
if (parent) {
|
||||
@ -155,7 +205,7 @@ export default class TreeNode {
|
||||
// 这边不能直接使用 expanded,需要额外判断是否可以展开
|
||||
// 如果只使用 expanded,会漏掉不可以展开的情况,即在不可以展开的情况下,会触发展开
|
||||
if (this.expandable && !this._expanded) {
|
||||
this.expanded = true;
|
||||
this.setExpanded(true);
|
||||
}
|
||||
if (tryExpandParents) {
|
||||
this.expandParents();
|
||||
@ -188,7 +238,7 @@ export default class TreeNode {
|
||||
expandParents() {
|
||||
let p = this.node.parent;
|
||||
while (p) {
|
||||
this.tree.getTreeNode(p).expanded = true;
|
||||
this.tree.getTreeNode(p).setExpanded(true);
|
||||
p = p.parent;
|
||||
}
|
||||
}
|
||||
@ -226,8 +276,19 @@ export default class TreeNode {
|
||||
|
||||
readonly designer: Designer;
|
||||
readonly document: DocumentModel;
|
||||
constructor(readonly tree: Tree, readonly node: Node) {
|
||||
@obx.ref private _node: Node;
|
||||
get node() {
|
||||
return this._node;
|
||||
}
|
||||
constructor(readonly tree: Tree, node: Node) {
|
||||
this.document = node.document;
|
||||
this.designer = this.document.designer;
|
||||
this._node = node;
|
||||
}
|
||||
|
||||
setNode(node: Node) {
|
||||
if (this._node !== node) {
|
||||
this._node = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,13 +7,18 @@ export class Tree {
|
||||
|
||||
readonly root: TreeNode;
|
||||
|
||||
readonly id: string;
|
||||
|
||||
constructor(readonly document: DocumentModel) {
|
||||
this.root = this.getTreeNode(document.rootNode);
|
||||
this.id = document.id;
|
||||
}
|
||||
|
||||
getTreeNode(node: Node): TreeNode {
|
||||
if (this.treeNodesMap.has(node.id)) {
|
||||
return this.treeNodesMap.get(node.id)!;
|
||||
const tnode = this.treeNodesMap.get(node.id)!;
|
||||
tnode.setNode(node);
|
||||
return tnode;
|
||||
}
|
||||
|
||||
const treeNode = new TreeNode(this, node);
|
||||
@ -1,7 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import { OutlineMain } from '../main';
|
||||
import { observer } from '../../../globals';
|
||||
import { intl } from '../locale';
|
||||
import { observer } from '../../../globals/src';
|
||||
import { OutlineMain } from '../main';
|
||||
import TreeView from './tree';
|
||||
import './style.less';
|
||||
|
||||
@observer
|
||||
@ -25,7 +26,9 @@ export default class OutlinePane extends Component<{ editor: any }> {
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.main.master.currentTree) {
|
||||
const tree = this.main.master.currentTree;
|
||||
|
||||
if (!tree) {
|
||||
return (
|
||||
<div className="lc-outline-pane">
|
||||
<p className="lc-outline-notice">{intl('No opened document')}</p>
|
||||
@ -35,11 +38,8 @@ export default class OutlinePane extends Component<{ editor: any }> {
|
||||
|
||||
return (
|
||||
<div className="lc-outline-pane">
|
||||
<div
|
||||
ref={shell => this.main.mount(shell)}
|
||||
className="lc-outline-tree-container"
|
||||
>
|
||||
<TreeView tree={this.main.master.currentTree} />
|
||||
<div ref={shell => this.main.mount(shell)} className="lc-outline-tree-container">
|
||||
<TreeView key={tree.id} tree={tree} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
295
packages/plugin-outline-tree/src/views/style.less
Normal file
295
packages/plugin-outline-tree/src/views/style.less
Normal file
@ -0,0 +1,295 @@
|
||||
.lc-outline-pane {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
> .lc-outline-tree-container {
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.lc-outline-tree {
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
user-select: none;
|
||||
|
||||
.tree-node-branches::before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: 1px solid transparent;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 6px;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.tree-node-branches::before {
|
||||
border-left-color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.insertion {
|
||||
pointer-events: none !important;
|
||||
border: 1px dashed var(--color-brand-light);
|
||||
height: 18px;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.condition-group-container {
|
||||
border-bottom: 1px solid #7b605b;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: 0.5px solid #7b605b;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
}
|
||||
>.condition-group-title {
|
||||
text-align: center;
|
||||
background-color: #7b605b;
|
||||
height: 14px;
|
||||
> .lc-title {
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
transform-origin: top;
|
||||
color: white;
|
||||
text-shadow: 0px 0px 2px black;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tree-node-slots {
|
||||
border-bottom: 1px solid rgb(144, 94, 190);
|
||||
position: relative;
|
||||
&:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
border-left: 0.5px solid rgb(144, 94, 190);
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
z-index: 2;
|
||||
}
|
||||
>.tree-node-slots-title {
|
||||
text-align: center;
|
||||
background-color: rgb(144, 94, 190);
|
||||
height: 14px;
|
||||
> .lc-title {
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
transform-origin: top;
|
||||
color: white;
|
||||
text-shadow: 0px 0px 2px black;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
.tree-node-expand-btn {
|
||||
width: 12px;
|
||||
line-height: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: color 200ms ease;
|
||||
color: var(--color-icon-normal);
|
||||
&:hover {
|
||||
color: var(--color-icon-hover);
|
||||
}
|
||||
> svg {
|
||||
transform-origin: center;
|
||||
transform: rotate(-90deg);
|
||||
transition: transform 100ms ease;
|
||||
}
|
||||
margin-right: 4px;
|
||||
}
|
||||
.tree-node-expand-placeholder {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.tree-node-icon {
|
||||
transform: translateZ(0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 4px;
|
||||
color: var(--color-text);
|
||||
|
||||
& > svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-title {
|
||||
font-size: var(--font-size-text);
|
||||
cursor: pointer;
|
||||
background: var(--color-pane-background);
|
||||
border-bottom: 1px solid var(--color-line-normal, rgba(31, 56, 88, 0.1));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
transform: translateZ(0);
|
||||
padding-right: 5px;
|
||||
& > :first-child {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.tree-node-title-label {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
overflow: visible;
|
||||
margin-right: 5px;
|
||||
|
||||
.tree-node-title-input {
|
||||
flex: 1;
|
||||
border: 1px solid var(--color-brand-light);
|
||||
background-color: var(--color-pane-background);
|
||||
color: var(--color-text);
|
||||
line-height: 18px;
|
||||
padding: 2px;
|
||||
outline: none;
|
||||
margin-left: -3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-hide-btn, .tree-node-lock-btn {
|
||||
opacity: 0;
|
||||
color: var(--color-text);
|
||||
line-height: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 22px;
|
||||
&:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.tree-node-hide-btn, .tree-node-lock-btn {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
&.editing {
|
||||
& > .tree-node-hide-btn, & >.tree-node-lock-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-tag {
|
||||
margin-left: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 0;
|
||||
&.cond {
|
||||
color: rgb(179, 52, 6);
|
||||
}
|
||||
&.loop {
|
||||
color: rgb(103, 187, 187);
|
||||
}
|
||||
&.slot {
|
||||
color: rgb(211, 90, 211);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-root {
|
||||
> .tree-node-title {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
& > .tree-node-title > .tree-node-expand-btn > svg {
|
||||
transform: rotate(0);
|
||||
}
|
||||
}
|
||||
|
||||
&.hovering > .tree-node-title {
|
||||
background: var(--color-block-background-light);
|
||||
}
|
||||
|
||||
// 选中节点处理
|
||||
&.selected {
|
||||
& > .tree-node-title {
|
||||
background: var(--color-block-background-shallow);
|
||||
}
|
||||
|
||||
& > .tree-node-branches::before {
|
||||
border-left-color: var(--color-brand-light);
|
||||
}
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
.tree-node-title-label {
|
||||
color: #9b9b9b;
|
||||
}
|
||||
& > .tree-node-title > .tree-node-hide-btn {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.tree-node-branches {
|
||||
.tree-node-hide-btn {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.condition-flow {
|
||||
& > .tree-node-title > .tree-node-hide-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
&.hidden > .tree-node-title > .tree-node-hide-btn {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.locked {
|
||||
& > .tree-node-title > .tree-node-lock-btn {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.tree-node-branches {
|
||||
.tree-node-lock-btn, .tree-node-hide-btn {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理拖入节点
|
||||
&.dropping {
|
||||
& > .tree-node-branches::before {
|
||||
border-left: 1px solid var(--color-brand);
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-branches {
|
||||
padding-left: 12px;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
packages/plugin-outline-tree/src/views/tree-branches.tsx
Normal file
114
packages/plugin-outline-tree/src/views/tree-branches.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
import { observer, Title } from '../../../globals';
|
||||
import { Component } from 'react';
|
||||
import TreeNode from '../tree-node';
|
||||
import TreeNodeView from './tree-node';
|
||||
import ExclusiveGroup from '../../../designer/src/designer/document/node/exclusive-group';
|
||||
import { intl } from '../locale';
|
||||
|
||||
@observer
|
||||
export default class TreeBranches extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const treeNode = this.props.treeNode;
|
||||
const { expanded } = treeNode;
|
||||
|
||||
if (!expanded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tree-node-branches">
|
||||
<TreeNodeSlots treeNode={treeNode} />
|
||||
<TreeNodeChildren treeNode={treeNode} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class TreeNodeChildren extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
let children: any = [];
|
||||
let groupContents: any[] = [];
|
||||
let currentGrp: ExclusiveGroup;
|
||||
const endGroup = () => {
|
||||
if (groupContents.length > 0) {
|
||||
children.push(
|
||||
<div key={currentGrp.id} className="condition-group-container" data-id={currentGrp.firstNode.id}>
|
||||
<div className="condition-group-title">
|
||||
<Title title={currentGrp.title} />
|
||||
</div>
|
||||
{groupContents}
|
||||
</div>,
|
||||
);
|
||||
groupContents = [];
|
||||
}
|
||||
};
|
||||
const { dropIndex } = treeNode;
|
||||
treeNode.children?.forEach((child, index) => {
|
||||
const { conditionGroup } = child.node;
|
||||
if (conditionGroup !== currentGrp) {
|
||||
endGroup();
|
||||
}
|
||||
|
||||
if (conditionGroup) {
|
||||
currentGrp = conditionGroup;
|
||||
if (index === dropIndex) {
|
||||
if (groupContents.length > 0) {
|
||||
groupContents.push(<div key="insertion" className="insertion" />);
|
||||
} else {
|
||||
children.push(<div key="insertion" className="insertion" />);
|
||||
}
|
||||
}
|
||||
groupContents.push(<TreeNodeView key={child.id} treeNode={child} />);
|
||||
} else {
|
||||
if (index === dropIndex) {
|
||||
children.push(<div key="insertion" className="insertion" />);
|
||||
}
|
||||
children.push(<TreeNodeView key={child.id} treeNode={child} />);
|
||||
}
|
||||
});
|
||||
endGroup();
|
||||
if (dropIndex != null && dropIndex === treeNode.children?.length) {
|
||||
children.push(<div key="insertion" className="insertion" />);
|
||||
}
|
||||
|
||||
return <div className="tree-node-children">{children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class TreeNodeSlots extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
if (!treeNode.isSlotContainer()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="tree-node-slots">
|
||||
<div className="tree-node-slots-title">
|
||||
<Title title={{ type: 'i18n', intl: intl('Slots')}} />
|
||||
</div>
|
||||
{treeNode.slots.map(tnode => (
|
||||
<TreeNodeView key={tnode.id} treeNode={tnode} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
43
packages/plugin-outline-tree/src/views/tree-node.tsx
Normal file
43
packages/plugin-outline-tree/src/views/tree-node.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '../../../globals';
|
||||
import TreeNode from '../tree-node';
|
||||
import TreeTitle from './tree-title';
|
||||
import TreeBranches from './tree-branches';
|
||||
|
||||
@observer
|
||||
export default class TreeNodeView extends Component<{ treeNode: TreeNode }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const className = classNames('tree-node', {
|
||||
// 是否展开
|
||||
expanded: treeNode.expanded,
|
||||
// 是否悬停中
|
||||
hovering: treeNode.hovering,
|
||||
// 是否选中的
|
||||
selected: treeNode.selected,
|
||||
// 是否隐藏的
|
||||
hidden: treeNode.hidden,
|
||||
// 是否忽略的
|
||||
// ignored: treeNode.ignored,
|
||||
// 是否锁定的
|
||||
locked: treeNode.locked,
|
||||
// 是否投放响应
|
||||
dropping: treeNode.dropIndex != null,
|
||||
'is-root': treeNode.isRoot(),
|
||||
'condition-flow': treeNode.node.conditionGroup != null,
|
||||
// highlight: treeNode.isResponseDropping() && treeNode.dropIndex == null,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className} data-id={treeNode.id}>
|
||||
<TreeTitle treeNode={treeNode} />
|
||||
<TreeBranches treeNode={treeNode} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
199
packages/plugin-outline-tree/src/views/tree-title.tsx
Normal file
199
packages/plugin-outline-tree/src/views/tree-title.tsx
Normal file
@ -0,0 +1,199 @@
|
||||
import { Component, KeyboardEvent, FocusEvent, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer, createIcon, Title, EmbedTip } from '../../../globals';
|
||||
import { IconArrowRight } from '../icons/arrow-right';
|
||||
import { IconEyeClose } from '../icons/eye-close';
|
||||
import { IconLock } from '../icons/lock';
|
||||
import { IconUnlock } from '../icons/unlock';
|
||||
import { intl } from '../locale';
|
||||
import TreeNode from '../tree-node';
|
||||
import { IconEye } from '../icons/eye';
|
||||
import { IconCond } from '../icons/cond';
|
||||
import { IconLoop } from '../icons/loop';
|
||||
import { IconSlot } from '../icons/slot';
|
||||
|
||||
@observer
|
||||
export default class TreeTitle extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
state = {
|
||||
editing: false,
|
||||
};
|
||||
|
||||
private enableEdit = () => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
});
|
||||
};
|
||||
|
||||
private cancelEdit() {
|
||||
this.setState({
|
||||
editing: false,
|
||||
});
|
||||
}
|
||||
|
||||
private saveEdit = (e: FocusEvent<HTMLInputElement> | KeyboardEvent<HTMLInputElement>) => {
|
||||
const { treeNode } = this.props;
|
||||
treeNode.setTitleLabel((e.target as HTMLInputElement).value || '');
|
||||
this.cancelEdit();
|
||||
};
|
||||
|
||||
private handleKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.keyCode === 13) {
|
||||
this.saveEdit(e);
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
this.cancelEdit();
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate() {
|
||||
// TODO:
|
||||
/*
|
||||
const { current } = this.inputRef;
|
||||
if (current) {
|
||||
current.select();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const { editing } = this.state;
|
||||
const isCNode = !treeNode.isRoot();
|
||||
const isNodeParent = treeNode.node.isNodeParent;
|
||||
let style: any;
|
||||
if (isCNode) {
|
||||
const depth = treeNode.depth;
|
||||
const indent = depth * 12;
|
||||
style = {
|
||||
paddingLeft: indent,
|
||||
marginLeft: -indent,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('tree-node-title', {
|
||||
editing,
|
||||
})}
|
||||
ref={ref => treeNode.mount(ref)}
|
||||
style={style}
|
||||
onClick={treeNode.node.conditionGroup ? () => treeNode.node.setConditionalVisible() : undefined}
|
||||
>
|
||||
{isCNode && <ExpandBtn treeNode={treeNode} />}
|
||||
<div className="tree-node-icon">{createIcon(treeNode.icon)}</div>
|
||||
<div className="tree-node-title-label" onDoubleClick={isNodeParent ? this.enableEdit : undefined}>
|
||||
{editing ? (
|
||||
<input
|
||||
className="tree-node-title-input"
|
||||
defaultValue={treeNode.titleLabel}
|
||||
onBlur={this.saveEdit}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
/>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Title title={treeNode.title} />
|
||||
{treeNode.node.slotFor && (<a className="tree-node-tag slot">
|
||||
{/* todo: click redirect to prop */}
|
||||
<IconSlot />
|
||||
<EmbedTip>{intl('Slot for {prop}', { prop: treeNode.node.slotFor.key })}</EmbedTip>
|
||||
</a>)}
|
||||
{treeNode.node.hasLoop() && (
|
||||
<a className="tree-node-tag loop">
|
||||
{/* todo: click todo something */}
|
||||
<IconLoop />
|
||||
<EmbedTip>{intl('Loop')}</EmbedTip>
|
||||
</a>
|
||||
)}
|
||||
{treeNode.node.hasCondition() && !treeNode.node.conditionGroup && (
|
||||
<a className="tree-node-tag cond">
|
||||
{/* todo: click todo something */}
|
||||
<IconCond />
|
||||
<EmbedTip>{intl('Conditional')}</EmbedTip>
|
||||
</a>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
{isCNode && isNodeParent && <HideBtn treeNode={treeNode} />}
|
||||
{isCNode && isNodeParent && <LockBtn treeNode={treeNode} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class LockBtn extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
return (
|
||||
<div
|
||||
className="tree-node-lock-btn"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
treeNode.setLocked(!treeNode.locked);
|
||||
}}
|
||||
>
|
||||
{treeNode.locked ? <IconLock /> : <IconUnlock />}
|
||||
<EmbedTip>{treeNode.locked ? intl('Unlock') : intl('Lock')}</EmbedTip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class HideBtn extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
return (
|
||||
<div
|
||||
className="tree-node-hide-btn"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
treeNode.setHidden(!treeNode.hidden);
|
||||
}}
|
||||
>
|
||||
{treeNode.hidden ? <IconEyeClose /> : <IconEye />}
|
||||
<EmbedTip>{treeNode.hidden ? intl('Show') : intl('Hide')}</EmbedTip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class ExpandBtn extends Component<{
|
||||
treeNode: TreeNode;
|
||||
}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
if (!treeNode.expandable) {
|
||||
return <i className="tree-node-expand-placeholder" />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="tree-node-expand-btn"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
treeNode.setExpanded(!treeNode.expanded);
|
||||
}}
|
||||
>
|
||||
<IconArrowRight size="small" />
|
||||
<EmbedTip>{treeNode.expanded ? intl('Collapse') : intl('Expand')}</EmbedTip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
@observer
|
||||
export default class TreeView extends React.Component {
|
||||
private ref = React.createRef<HTMLDivElement>();
|
||||
private dispose?: () => void;
|
||||
import { Component } from 'react';
|
||||
import { observer } from '../../../globals';
|
||||
import { Tree } from '../tree';
|
||||
import TreeNodeView from './tree-node';
|
||||
|
||||
@observer
|
||||
export default class TreeView extends Component<{ tree: Tree }> {
|
||||
/*
|
||||
hover(e: any) {
|
||||
const treeNode = tree.getTreeNodeByEvent(e);
|
||||
|
||||
@ -68,12 +71,13 @@ export default class TreeView extends React.Component {
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
render() {
|
||||
const { tree } = this.props;
|
||||
const root = tree.root;
|
||||
return (
|
||||
<div className="my-outline-tree">
|
||||
<div className="lc-outline-tree">
|
||||
<TreeNodeView key={root.id} treeNode={root} />
|
||||
</div>
|
||||
);
|
||||
Loading…
x
Reference in New Issue
Block a user