Skip to content

Commit 875bdfd

Browse files
author
FalkWolsky
committed
Sortable Display of Components
1 parent a81e175 commit 875bdfd

File tree

9 files changed

+142
-95
lines changed

9 files changed

+142
-95
lines changed

client/packages/lowcoder-core/lib/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ declare enum CompActionTypes {
454454
* broadcast other actions in comp tree structure.
455455
* used for encapsulate MultiBaseComp
456456
*/
457-
BROADCAST = "BROADCAST"
457+
BROADCAST = "BROADCAST",
458458
}
459459
type ExtraActionType = "layout" | "delete" | "add" | "modify" | "rename" | "recover" | "upgrade";
460460
type ActionExtraInfo = {

client/packages/lowcoder/src/comps/editorState.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { NameAndExposingInfo } from "./utils/exposingTypes";
1717
import { checkName } from "./utils/rename";
1818
import { trans } from "i18n";
1919
import { UiLayoutType } from "./comps/uiComp";
20-
import { getCollissionStatus, getEditorModeStatus } from "util/localStorageUtil";
20+
import { getCollisionStatus, getEditorModeStatus } from "util/localStorageUtil";
2121

2222
type RootComp = InstanceType<typeof RootCompTmp>;
2323

@@ -47,7 +47,7 @@ export class EditorState {
4747
readonly showPropertyPane: boolean = false;
4848
readonly selectedCompNames: Set<string> = new Set();
4949
readonly editorModeStatus: string = "";
50-
readonly collissionStatus: string = "";
50+
readonly collisionStatus: string = "";
5151
readonly isDragging: boolean = false;
5252
readonly draggingCompType: string = "button";
5353
readonly forceShowGrid: boolean = false; // show grid lines
@@ -65,12 +65,12 @@ export class EditorState {
6565
rootComp: RootComp,
6666
setEditorState: (fn: (editorState: EditorState) => EditorState) => void,
6767
initialEditorModeStatus: string = getEditorModeStatus(),
68-
initialCollissionStatus: string = getCollissionStatus()
68+
initialCollisionStatus: string = getCollisionStatus()
6969
) {
7070
this.rootComp = rootComp;
7171
this.setEditorState = setEditorState;
7272
this.editorModeStatus = initialEditorModeStatus;
73-
this.collissionStatus = initialCollissionStatus;
73+
this.collisionStatus = initialCollisionStatus;
7474
}
7575

7676
/**
@@ -134,12 +134,13 @@ export class EditorState {
134134

135135
uiCompInfoList(): Array<CompInfo> {
136136
const compMap = this.getAllUICompMap();
137-
return Object.values(compMap).map((item) => {
137+
return Object.entries(compMap).map(([key, item]) => {
138138
return {
139139
name: item.children.name.getView(),
140140
type: item.children.compType.getView(),
141141
data: item.children.comp.exposingValues,
142142
dataDesc: item.children.comp.exposingInfo().propertyDesc,
143+
key: key,
143144
};
144145
});
145146
}
@@ -355,8 +356,8 @@ export class EditorState {
355356
this.changeState({ editorModeStatus: newEditorModeStatus });
356357
}
357358

358-
setCollissionStatus(newCollissionStatus: string) {
359-
this.changeState({ collissionStatus: newCollissionStatus });
359+
setCollisionStatus(newCollisionStatus: string) {
360+
this.changeState({ collisionStatus: newCollisionStatus });
360361
}
361362

362363
setDragging(dragging: boolean) {
@@ -512,9 +513,10 @@ export class EditorState {
512513
getAppType(): UiLayoutType {
513514
return this.getUIComp().children.compType.getView();
514515
}
515-
getCollissionStatus(): string {
516-
return this.collissionStatus;
516+
getCollisionStatus(): string {
517+
return this.collisionStatus;
517518
}
519+
518520
}
519521
export const EditorContext = React.createContext<EditorState>(undefined as any);
520522

client/packages/lowcoder/src/layout/utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { UICompType } from "comps/uiCompRegistry";
22
import _ from "lodash";
3-
import React, { ReactElement, SyntheticEvent } from "react";
3+
import React, { ReactElement, SyntheticEvent, useContext } from "react";
44
import { DraggableEvent } from "react-draggable";
55
import { PositionParams } from "./calculateUtils";
66
import { draggingUtils } from "./draggingUtils";
77
import { GridLayoutProps, ResizeHandleAxis } from "./gridLayoutPropTypes";
88

9+
import { getCollisionStatus } from "util/localStorageUtil";
10+
911
export type LayoutItem = {
1012
w: number;
1113
h: number;
@@ -169,7 +171,12 @@ export function collides(l1: LayoutItem, l2: LayoutItem): boolean {
169171
if (l1.y + l1.h <= l2.y) return false; // l1 is above l2
170172
if (l1.y >= l2.y + l2.h) return false; // l1 is below l2
171173

172-
return true; // boxes overlap
174+
if (getCollisionStatus() === "true") {
175+
return false;
176+
}
177+
else {
178+
return true; // boxes overlap
179+
}
173180
}
174181

175182
/**

client/packages/lowcoder/src/pages/common/header.tsx

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
preview,
1111
} from "constants/routesURL";
1212
import { User } from "constants/userConstants";
13-
import { Switch } from "antd";
1413
import {
1514
CommonTextLabel,
1615
CustomModal,
@@ -54,7 +53,6 @@ import { AppPermissionDialog } from "../../components/PermissionDialog/AppPermis
5453
import { getBrandingConfig } from "../../redux/selectors/configSelectors";
5554
import { messageInstance } from "lowcoder-design";
5655
import { EditorContext } from "../../comps/editorState";
57-
import { SwitchChangeEventHandler } from "antd/es/switch";
5856

5957
const StyledLink = styled.a`
6058
display: flex;
@@ -293,28 +291,18 @@ export type ToggleEditorModeStatus = (
293291
editorModeStatus?: EditorModeStatus
294292
) => void;
295293

296-
/*
297-
// export type EnabledCollissionStatus = "true" | "false";
298-
export type ToggleCollissionStatus = (
299-
collissionStatus?: EnabledCollissionStatus
300-
) => void;
301-
*/
302-
303294
type HeaderProps = {
304295
panelStatus: PanelStatus;
305296
togglePanel: TogglePanel;
306297
editorModeStatus: EditorModeStatus;
307298
toggleEditorModeStatus: ToggleEditorModeStatus;
308-
// collissionStatus: EnabledCollissionStatus;
309-
// toggleCollissionStatus: ToggleCollissionStatus;
310299
};
311300

312301
// header in editor page
313302
export default function Header(props: HeaderProps) {
314303
const editorState = useContext(EditorContext);
315304
const { togglePanel } = props;
316305
const { toggleEditorModeStatus } = props;
317-
// const { toggleCollissionStatus } = props;
318306
const { left, bottom, right } = props.panelStatus;
319307
const user = useSelector(getUser);
320308
const application = useSelector(currentApplication);
@@ -347,24 +335,6 @@ export default function Header(props: HeaderProps) {
347335
},
348336
];
349337

350-
// const collissionOptions = [
351-
// {
352-
// label: trans("header.editorMode_layout"),
353-
// key: "editorModeSelector_layout",
354-
// value: "tru",
355-
// },
356-
// {
357-
// label: trans("header.editorMode_logic"),
358-
// key: "editorModeSelector_logic",
359-
// value: "logic",
360-
// },
361-
// {
362-
// label: trans("header.editorMode_both"),
363-
// key: "editorModeSelector_both",
364-
// value: "both",
365-
// },
366-
// ];
367-
368338
const onEditorStateValueChange = ({
369339
target: { value },
370340
}: RadioChangeEvent) => {

client/packages/lowcoder/src/pages/editor/LeftContent.tsx

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
UnfoldIcon,
1616
UnShow,
1717
} from "lowcoder-design";
18-
import React, { ReactNode, useCallback, useContext, useMemo, useState } from "react";
18+
import React, { ReactNode, useCallback, useContext, useMemo, useState, useEffect } from "react";
1919
import { hookCompCategory } from "comps/hooks/hookCompTypes";
2020
import _ from "lodash";
2121
import styled from "styled-components";
@@ -32,6 +32,7 @@ import { UICompType } from "comps/uiCompRegistry";
3232
import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents";
3333
import { DataNode, EventDataNode } from "antd/lib/tree";
3434
import { isAggregationApp } from "util/appUtils";
35+
import cloneDeep from 'lodash/cloneDeep';
3536

3637
const CollapseTitleWrapper = styled.div`
3738
display: flex;
@@ -408,17 +409,94 @@ export const LeftContent = (props: LeftContentProps) => {
408409
);
409410
};
410411

411-
const getTreeUI = (type: TreeUIKey) => {
412-
const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]);
412+
const [componentTreeData, setComponentTreeData] = useState<NodeItem[]>([]);
413+
const [modalsTreeData, setModalsTreeData] = useState<NodeItem[]>([]);
414+
415+
useEffect(() => {
416+
const compData = getTreeUIData(TreeUIKey.Components);
417+
setComponentTreeData(compData);
418+
}, [editorState]);
419+
420+
useEffect(() => {
421+
const modalsData = getTreeUIData(TreeUIKey.Modals);
422+
setModalsTreeData(modalsData);
423+
}, [editorState]);
424+
425+
const getTreeUIData = (type: TreeUIKey) => {
413426
const tree =
414427
type === TreeUIKey.Components
415428
? editorState.getUIComp().getTree()
416429
: editorState.getHooksComp().getUITree();
417430
const explorerData: NodeItem[] = getTree(tree, []);
431+
return explorerData;
432+
}
433+
434+
interface DropInfo {
435+
node: { key: string; pos: string };
436+
dragNode: { key: string; pos: string };
437+
}
438+
439+
const handleDragEnter = (info: { node?: any; expandedKeys?: any; }) => {
440+
// Assuming 'info' has a property 'expandedKeys' which is an array of keys
441+
const { expandedKeys } = info;
442+
if (!expandedKeys.includes(info.node.key)) {
443+
setExpandedKeys(expandedKeys);
444+
}
445+
};
446+
447+
const handleDrop = (info: { node: { key: any; pos: string; }; dragNode: { key: any; pos: string; }; }, type: TreeUIKey) => {
448+
const dropPos = info.node.pos.split('-');
449+
const dragPos = info.dragNode.pos.split('-');
450+
451+
if (dropPos.length === dragPos.length) {
452+
setComponentTreeData(prevData => {
453+
let newTreeData = cloneDeep(prevData);
454+
const dropIndex = Number(dropPos[dropPos.length - 1]);
455+
const dragIndex = Number(dragPos[dragPos.length - 1]);
456+
const parentNodePos = dropPos.slice(0, -1).join('-');
457+
458+
// TODO: handle drag and drop for childen of root (container components for example)
459+
// findNodeByPos does not work yet
460+
const parentNode = parentNodePos === "0" ? { children: newTreeData } : findNodeByPos(newTreeData, parentNodePos);
461+
462+
console.log('parentNode', parentNode);
463+
464+
if (parentNode && parentNode.children) {
465+
const draggedNodeIndex = parentNode.children.findIndex(node => node.key === info.dragNode.key);
466+
if (draggedNodeIndex !== -1) {
467+
const [draggedNode] = parentNode.children.splice(draggedNodeIndex, 1);
468+
parentNode.children.splice(dropIndex > dragIndex ? dropIndex - 1 : dropIndex, 0, draggedNode);
469+
}
470+
}
471+
472+
return newTreeData;
473+
});
474+
}
475+
};
476+
477+
const findNodeByPos = (nodes: NodeItem[], pos: string): { children: NodeItem[] } => {
478+
const posArr = pos.split('-').map(p => Number(p));
479+
let currentNode = { children: nodes };
480+
for (let i = 0; i < posArr.length; i++) {
481+
currentNode = currentNode.children[posArr[i]];
482+
}
483+
return currentNode;
484+
};
485+
486+
const getTreeUI = (type: TreeUIKey) => {
487+
// here the components get sorted by name
488+
// TODO: sort by category
489+
// TODO: sort by Types etc.
490+
const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]);
491+
/* const tree =
492+
type === TreeUIKey.Components
493+
? editorState.getUIComp().getTree()
494+
: editorState.getHooksComp().getUITree();
495+
const explorerData: NodeItem[] = getTree(tree, []); */
418496
let selectedKeys = [];
419497
if (editorState.selectedCompNames.size === 1) {
420498
const key = Object.keys(editorState.selectedComps())[0];
421-
const parentKeys = getParentNodeKeysByKey(explorerData, key);
499+
const parentKeys = getParentNodeKeysByKey(type === TreeUIKey.Components ? componentTreeData : modalsTreeData, key);
422500
if (parentKeys && parentKeys.length) {
423501
let needSet = false;
424502
parentKeys.forEach((key) => {
@@ -433,12 +511,11 @@ export const LeftContent = (props: LeftContentProps) => {
433511

434512
return (
435513
<DirectoryTreeStyle
436-
treeData={explorerData}
437-
// icon={(props: NodeItem) => props.type && (CompStateIcon[props.type] || <LeftCommon />)}
514+
draggable={type === TreeUIKey.Components ? true : false}
515+
onDragEnter={handleDragEnter}
516+
onDrop={(info) => handleDrop(info, type)}
517+
treeData={type === TreeUIKey.Components ? componentTreeData : modalsTreeData}
438518
icon={(props: any) => props.type && (CompStateIcon[props.type as UICompType] || <LeftCommon />)}
439-
// switcherIcon={({ expanded }: { expanded: boolean }) =>
440-
// expanded ? <FoldedIcon /> : <UnfoldIcon />
441-
// }
442519
switcherIcon={(props: any) =>
443520
props.expanded ? <FoldedIcon /> : <UnfoldIcon />
444521
}
@@ -455,15 +532,15 @@ export const LeftContent = (props: LeftContentProps) => {
455532
if (isAggregationApp(editorState.getAppType())) {
456533
return;
457534
}
458-
return getTreeUI(TreeUIKey.Components);
459-
}, [editorState, uiCollapseClick, expandedKeys, showData]);
460-
535+
return getTreeUI(TreeUIKey.Components); // Pass componentTreeData
536+
}, [editorState, uiCollapseClick, expandedKeys, showData, componentTreeData]);
537+
461538
const modalsCollapse = useMemo(() => {
462539
if (isAggregationApp(editorState.getAppType())) {
463540
return;
464541
}
465-
return getTreeUI(TreeUIKey.Modals);
466-
}, [editorState, uiCollapseClick, expandedKeys, showData]);
542+
return getTreeUI(TreeUIKey.Modals); // Pass modalsTreeData
543+
}, [editorState, uiCollapseClick, expandedKeys, showData, modalsTreeData]);
467544

468545
const bottomResCollapse = useMemo(() => {
469546
return editorState
@@ -549,4 +626,4 @@ export const LeftContent = (props: LeftContentProps) => {
549626
</LeftContentTabs>
550627
</LeftContentWrapper>
551628
);
552-
};
629+
};

client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
LeftPanel,
88
MiddlePanel,
99
} from "pages/common/styledComponent";
10-
import { getPanelStatus, getEditorModeStatus, getPanelStyle, getCollissionStatus } from "util/localStorageUtil";
10+
import { getPanelStatus, getEditorModeStatus, getPanelStyle, getCollisionStatus } from "util/localStorageUtil";
1111
import { BottomSkeleton } from "pages/editor/bottom/BottomContent";
1212
import RightPanel from "pages/editor/right/RightPanel";
1313
import _ from "lodash";
@@ -48,7 +48,7 @@ export const EditorLoadingSpin = (props: { height?: string | number }) => {
4848
export default function EditorSkeletonView() {
4949
const panelStatus = getPanelStatus();
5050
const editorModeStatus = getEditorModeStatus();
51-
const collissionStatus = getCollissionStatus();
51+
const collisionStatus = getCollisionStatus();
5252
const panelStyle = getPanelStyle();
5353
const isUserViewMode = useUserViewMode();
5454
const isTemplate = useTemplateViewMode();
@@ -70,8 +70,8 @@ export default function EditorSkeletonView() {
7070
<SiderStyled />
7171
{panelStatus.left && (
7272
<LeftPanel
73-
collissionStatus={collissionStatus}
74-
toggleCollissionStatus={_.noop}
73+
collisionStatus={collisionStatus}
74+
toggleCollisionStatus={_.noop}
7575
>
7676
<StyledSkeleton active paragraph={{ rows: 10 }} />
7777
</LeftPanel>

0 commit comments

Comments
 (0)