Skip to content

Commit e33878f

Browse files
sarikesunlei
authored and
sunlei
committed
fix: keep folder fold status when drag order
(cherry picked from commit 0e203a30159eed77375bffecf21d78ae6213fc96)
1 parent ff0ba89 commit e33878f

File tree

7 files changed

+72
-21
lines changed

7 files changed

+72
-21
lines changed

client/packages/openblocks/src/components/DraggableTree/DraggableItem.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ const Wrapper = styled.div<{
2121
margin-bottom: ${(props) => props.positionLineHeight ?? 4}px;
2222
display: flex;
2323
/* padding: 0 8px; */
24-
background-color: #ffffff;
2524
align-items: center;
2625
opacity: ${(props) => (props.dragging ? "0.5" : 1)};
2726

client/packages/openblocks/src/components/DraggableTree/DraggableTree.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
useSensors,
99
} from "@dnd-kit/core";
1010
import _ from "lodash";
11-
import { useContext, useState } from "react";
11+
import { useContext, useMemo, useState } from "react";
1212
import styled from "styled-components";
1313
import { DraggableTreeContext, DraggableTreeContextValue } from "./DraggableTreeContext";
1414
import DraggableMenuItem from "./DroppableMenuItem";
@@ -130,7 +130,8 @@ function MenuItemList(props: IMenuItemListProps) {
130130
<DragOverlay dropAnimation={null}>
131131
{active && (
132132
<DraggableMenuItem
133-
defaultFold={contextValue.showSubInDragOverlay === false}
133+
isOverlay
134+
forceFold={contextValue.showSubInDragOverlay === false}
134135
path={[]}
135136
item={active.node}
136137
renderContent={renderItemContent}
@@ -142,13 +143,15 @@ function MenuItemList(props: IMenuItemListProps) {
142143
);
143144
}
144145

145-
interface DraggableTreeProps<T = any> extends DraggableTreeContextValue {
146+
interface DraggableTreeProps<T = any>
147+
extends Omit<DraggableTreeContextValue, "foldedStatus" | "toggleFold"> {
146148
node: DraggableTreeNode<T>;
147149
renderItemContent: (params: DraggableTreeNodeItemRenderProps<T>) => React.ReactNode;
148150
}
149151

150152
export function DraggableTree<T = any>(props: DraggableTreeProps<T>) {
151-
const { node, renderItemContent, ...contextValue } = props;
153+
const { node, renderItemContent, ...otherProps } = props;
154+
const [foldedStatus, setFoldedState] = useState<Record<string, boolean>>({});
152155

153156
const getItemByPath = (path: number[], scope?: DraggableTreeNode[]): DraggableTreeNode => {
154157
if (!scope) {
@@ -170,6 +173,24 @@ export function DraggableTree<T = any>(props: DraggableTreeProps<T>) {
170173
return getItemListByPath(path.slice(1), root.items[path[0]]);
171174
};
172175

176+
const toggleFold = (id: string) => {
177+
// toggle setFoldedState by id in foldedStatus
178+
setFoldedState((prev) => {
179+
const newFoldedStatus: Record<string, boolean> = { ...prev };
180+
newFoldedStatus[id] = !newFoldedStatus[id];
181+
return newFoldedStatus;
182+
});
183+
};
184+
185+
const contextValue = useMemo<DraggableTreeContextValue>(
186+
() => ({
187+
...otherProps,
188+
toggleFold,
189+
foldedStatus,
190+
}),
191+
[foldedStatus, otherProps]
192+
);
193+
173194
return (
174195
<DraggableTreeContext.Provider value={contextValue}>
175196
<MenuItemList

client/packages/openblocks/src/components/DraggableTree/DraggableTreeContext.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ import React from "react";
22

33
export interface DraggableTreeContextValue {
44
disable?: boolean;
5+
unfoldAll?: boolean;
56
itemHeight?: number;
67
positionLineHeight?: number;
78
showPositionLineDot?: boolean;
89
positionLineDotDiameter?: number;
910
showSubInDragOverlay?: boolean;
1011
positionLineIndent?(path: number[], dropInAsSub: boolean): number;
12+
13+
toggleFold(id: string): void;
14+
foldedStatus: Record<string, boolean>;
1115
}
12-
export const DraggableTreeContext = React.createContext<DraggableTreeContextValue>({});
16+
export const DraggableTreeContext = React.createContext<DraggableTreeContextValue>({
17+
toggleFold: () => {},
18+
foldedStatus: {},
19+
});

client/packages/openblocks/src/components/DraggableTree/DroppableMenuItem.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useDraggable, useDroppable } from "@dnd-kit/core";
2-
import { Fragment, useContext, useState } from "react";
2+
import { Fragment, useContext } from "react";
33
import styled from "styled-components";
44
import DraggableItem from "./DraggableItem";
55
import { DraggableTreeContext } from "./DraggableTreeContext";
@@ -14,10 +14,12 @@ const DraggableMenuItemWrapper = styled.div`
1414
interface IDraggableMenuItemProps {
1515
path: number[];
1616
item: DraggableTreeNode;
17-
defaultFold?: boolean;
17+
forceFold?: boolean;
1818
dropInAsSub?: boolean;
1919
activeNode?: DraggableTreeNode;
2020
disabled?: boolean;
21+
// current item is used as overlay or not
22+
isOverlay?: boolean;
2123
disableDropIn?: boolean;
2224
parentDragging?: boolean;
2325
onDelete?: (path: number[]) => void;
@@ -33,17 +35,18 @@ export default function DraggableMenuItem(props: IDraggableMenuItemProps) {
3335
disabled,
3436
parentDragging,
3537
disableDropIn,
36-
defaultFold = false,
38+
forceFold = false,
3739
dropInAsSub = true,
40+
isOverlay = false,
3841
// onAddSubMenu,
3942
onDelete,
4043
renderContent,
4144
} = props;
4245

43-
const id = path.join("_");
46+
const id = item.id ?? path.join("_");
4447
const items = item.items;
45-
const [isFold, setIsFold] = useState(defaultFold);
4648
const context = useContext(DraggableTreeContext);
49+
const isFold = (forceFold || context.foldedStatus[id]) && !context.unfoldAll;
4750

4851
const dragData: IDragData = {
4952
path,
@@ -87,7 +90,7 @@ export default function DraggableMenuItem(props: IDraggableMenuItemProps) {
8790
)}
8891
<DraggableItem
8992
path={path}
90-
id={path.join("_")}
93+
id={id}
9194
dropInAsSub={dropInAsSub && canDropIn !== false}
9295
isOver={isOver}
9396
ref={(node) => {
@@ -100,11 +103,12 @@ export default function DraggableMenuItem(props: IDraggableMenuItemProps) {
100103
node: item,
101104
isOver,
102105
path,
106+
isOverlay,
103107
hasChildren: items.length > 0,
104108
dragging: !!(isDragging || parentDragging),
105109
isFolded: isFold,
106110
onDelete: () => onDelete?.(path),
107-
onToggleFold: () => setIsFold(!isFold),
111+
onToggleFold: () => context.toggleFold(id),
108112
}) || null}
109113
</DraggableItem>
110114
</DraggableMenuItemWrapper>

client/packages/openblocks/src/components/DraggableTree/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ export interface DraggableTreeNodeItemRenderProps<T = any> {
44
dragging: boolean;
55
isOver: boolean;
66
isFolded: boolean;
7+
isOverlay: boolean;
78
hasChildren: boolean;
89
onDelete: () => void;
910
onToggleFold: () => void;
1011
}
1112
export interface DraggableTreeNode<T = any> {
13+
// identity a node, will be used to save fold status
14+
id?: string;
1215
data?: T;
1316
canDropIn?: boolean | ((source: T) => boolean);
1417
canDropBefore?: boolean | ((source?: T) => boolean);

client/packages/openblocks/src/comps/comps/jsonComp/jsonEditorComp.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
type EditorView as EditorViewType,
2020
} from "base/codeEditor/codeMirror";
2121
import { useExtensions } from "base/codeEditor/extensions";
22+
import { getJsonFormatter } from "base/codeEditor/autoFormat";
2223

2324
/**
2425
* JsonEditor Comp

client/packages/openblocks/src/pages/editor/bottom/BottomSidebar.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export function BottomSidebar(props: BottomSidebarProps) {
149149
.map((i) => convertRefTree(i as InstanceType<typeof RefTreeComp>))
150150
.filter((i): i is DraggableTreeNode<BottomResComp> => !!i);
151151
const node: DraggableTreeNode<BottomResComp> = {
152+
id: bottomResComp?.id(),
152153
canDropBefore: (source) => {
153154
if (currentNodeType === BottomResTypeEnum.Folder) {
154155
return source?.type() === BottomResTypeEnum.Folder;
@@ -262,10 +263,11 @@ export function BottomSidebar(props: BottomSidebarProps) {
262263
)}
263264
<ScrollBar>
264265
{items.length > 0 && node ? (
265-
<div style={{ paddingTop: 4, paddingBottom: 16 }}>
266+
<div style={{ paddingTop: 4, paddingBottom: 100, overflow: "hidden" }}>
266267
<DraggableTree<BottomResComp>
267268
node={node}
268269
disable={!!search}
270+
unfoldAll={!!search}
269271
showSubInDragOverlay={false}
270272
showPositionLineDot
271273
positionLineDotDiameter={4}
@@ -314,7 +316,12 @@ export function BottomSidebar(props: BottomSidebarProps) {
314316
);
315317
}
316318

317-
const ColumnDiv = styled.div<{ $color?: boolean; level: number; foldable: boolean }>`
319+
const ColumnDiv = styled.div<{
320+
$color?: boolean;
321+
level: number;
322+
foldable: boolean;
323+
isOverlay: boolean;
324+
}>`
318325
width: 100%;
319326
height: 25px;
320327
display: flex;
@@ -323,11 +330,12 @@ const ColumnDiv = styled.div<{ $color?: boolean; level: number; foldable: boolea
323330
justify-content: center;
324331
padding: 0 15px 0 2px;
325332
padding-left: ${(props) => 2 + props.level * 20 + (props.foldable ? 0 : 14)}px;
326-
background-color: #ffffff;
333+
/* background-color: #ffffff; */
327334
/* margin: 2px 0; */
335+
background-color: ${(props) => (props.isOverlay ? "rgba(255, 255, 255, 0.11)" : "")};
328336
329337
&&& {
330-
background-color: ${(props) => (props.$color ? "#f2f7fc" : null)};
338+
background-color: ${(props) => (props.$color && !props.isOverlay ? "#f2f7fc" : null)};
331339
}
332340
333341
:hover {
@@ -342,6 +350,7 @@ const ColumnDiv = styled.div<{ $color?: boolean; level: number; foldable: boolea
342350
color: #222222;
343351
margin-left: 0;
344352
font-size: 13px;
353+
padding-left: 0;
345354
346355
:hover {
347356
background-color: transparent;
@@ -394,7 +403,8 @@ interface BottomSidebarItemProps extends DraggableTreeNodeItemRenderProps {
394403
}
395404

396405
function BottomSidebarItem(props: BottomSidebarItemProps) {
397-
const { id, resComp, path, isFolded, onDelete, onCopy, onSelect, onToggleFold } = props;
406+
const { id, resComp, isOverlay, path, isFolded, onDelete, onCopy, onSelect, onToggleFold } =
407+
props;
398408
const [error, setError] = useState<string | undefined>(undefined);
399409
const [editing, setEditing] = useState(false);
400410
const editorState = useContext(EditorContext);
@@ -441,14 +451,20 @@ function BottomSidebarItem(props: BottomSidebarItemProps) {
441451
};
442452

443453
return (
444-
<ColumnDiv level={level} foldable={isFolder} onClick={handleClickItem} $color={isSelected}>
454+
<ColumnDiv
455+
level={level}
456+
foldable={isFolder}
457+
onClick={handleClickItem}
458+
$color={isSelected}
459+
isOverlay={isOverlay}
460+
>
445461
{isFolder && <FoldIconBtn>{!isFolded ? <FoldedIcon /> : <UnfoldIcon />}</FoldIconBtn>}
446462
{icon}
447463
<div style={{ flexGrow: 1, marginRight: "8px", width: "calc(100% - 62px)" }}>
448464
<EditText
449465
text={name}
450466
forceClickIcon={isFolder}
451-
disabled={!isSelected || readOnly}
467+
disabled={!isSelected || readOnly || isOverlay}
452468
onFinish={handleFinishRename}
453469
onChange={handleNameChange}
454470
onEditStateChange={(editing) => setEditing(editing)}
@@ -460,7 +476,7 @@ function BottomSidebarItem(props: BottomSidebarItemProps) {
460476
hasError={!!error}
461477
/>
462478
</div>
463-
{!readOnly && (
479+
{!readOnly && !isOverlay && (
464480
<EditPopover copy={!isFolder ? onCopy : undefined} del={onDelete}>
465481
<Icon tabIndex={-1} />
466482
</EditPopover>

0 commit comments

Comments
 (0)