@@ -2,7 +2,7 @@ import { SortableTree, TreeItems, TreeItemComponentProps, SimpleTreeItemWrapper
22import LinkPlusButton from "components/LinkPlusButton" ;
33import { BluePlusIcon , controlItem , ScrollBar } from "lowcoder-design" ;
44import { trans } from "i18n" ;
5- import React , { useMemo , useCallback , createContext , useContext , useState } from "react" ;
5+ import React , { useMemo , useCallback , createContext , useContext } from "react" ;
66import styled from "styled-components" ;
77import { NavCompType , NavListCompType , NavTreeItemData } from "./types" ;
88import MenuItem from "./MenuItem" ;
@@ -105,68 +105,42 @@ interface IMenuItemListProps {
105105
106106const menuItemLabel = trans ( "navigation.itemsDesc" ) ;
107107
108- type TreeChangeReason = { type : string } ;
109-
110108// Convert NavCompType[] to TreeItems format for dnd-kit-sortable-tree
111109function convertToTreeItems (
112110 items : NavCompType [ ] ,
113- basePath : number [ ] = [ ] ,
114- collapsedIds : Set < string > = new Set ( )
111+ basePath : number [ ] = [ ]
115112) : TreeItems < NavTreeItemData > {
116113 return items . map ( ( item , index ) => {
117114 const path = [ ...basePath , index ] ;
118115 // Use stable itemKey if available, fallback to path-based ID for backwards compatibility
119116 const itemKey = item . getItemKey ?.( ) || "" ;
120117 const id = itemKey || path . join ( "_" ) ;
121118 const subItems = item . getView ( ) . items || [ ] ;
119+ // Read collapsed state from the item itself
120+ const collapsed = item . getCollapsed ?.( ) ?? false ;
122121
123122 return {
124123 id,
125- collapsed : collapsedIds . has ( id ) ,
124+ collapsed,
126125 comp : item ,
127126 path : path ,
128127 children : subItems . length > 0
129- ? convertToTreeItems ( subItems , path , collapsedIds )
128+ ? convertToTreeItems ( subItems , path )
130129 : [ ] ,
131130 } ;
132131 } ) ;
133132}
134133
135- function extractCollapsedIds ( treeItems : TreeItems < NavTreeItemData > ) : Set < string > {
136- const ids = new Set < string > ( ) ;
137- const walk = ( items : TreeItems < NavTreeItemData > ) => {
138- items . forEach ( ( item ) => {
139- if ( item . collapsed ) {
140- ids . add ( String ( item . id ) ) ;
141- }
142- if ( item . children ?. length ) {
143- walk ( item . children ) ;
144- }
145- } ) ;
146- } ;
147- walk ( treeItems ) ;
148- return ids ;
149- }
150-
151134function MenuItemList ( props : IMenuItemListProps ) {
152135 const { items, onAddItem, onDeleteItem, onAddSubItem, onReorderItems } = props ;
153136
154- const [ collapsedIds , setCollapsedIds ] = useState < Set < string > > ( ( ) => new Set ( ) ) ;
155-
156137 // Convert items to tree format
157- const treeItems = useMemo ( ( ) => convertToTreeItems ( items , [ ] , collapsedIds ) , [ items , collapsedIds ] ) ;
138+ const treeItems = useMemo ( ( ) => convertToTreeItems ( items ) , [ items ] ) ;
158139
159- // Handle tree changes from drag and drop
140+ // Handle all tree changes ( drag/ drop, collapse/expand)
160141 const handleItemsChanged = useCallback (
161- ( newItems : TreeItems < NavTreeItemData > , reason : TreeChangeReason ) => {
162- // Persist collapsed/expanded state locally (SortableTree is controlled by `items` prop)
163- setCollapsedIds ( extractCollapsedIds ( newItems ) ) ;
164-
165- // Only rewrite the underlying nav structure when the tree structure actually changed.
166- // (If we rebuild on collapsed/expanded, it immediately resets the UI and looks like "toggle does nothing".)
167- if ( reason . type === "dropped" || reason . type === "removed" ) {
168- onReorderItems ( newItems ) ;
169- }
142+ ( newItems : TreeItems < NavTreeItemData > ) => {
143+ onReorderItems ( newItems ) ;
170144 } ,
171145 [ onReorderItems ]
172146 ) ;
@@ -227,14 +201,14 @@ export function menuPropertyView(itemsComp: NavListCompType) {
227201 return getItemListByPath ( path . slice ( 1 ) , root . getView ( ) [ path [ 0 ] ] . children . items ) ;
228202 } ;
229203
230- // Convert flat tree structure back to nested comp structure
204+ // Convert tree structure back to nested comp structure
231205 const handleReorderItems = ( newItems : TreeItems < NavTreeItemData > ) => {
232- // Build the new order from tree items
233206 const buildJsonFromTree = ( treeItems : TreeItems < NavTreeItemData > ) : any [ ] => {
234207 return treeItems . map ( ( item ) => {
235208 const jsonValue = item . comp . toJsonValue ( ) as Record < string , any > ;
236209 return {
237210 ...jsonValue ,
211+ collapsed : item . collapsed ?? false , // sync collapsed from tree item
238212 items : item . children && item . children . length > 0
239213 ? buildJsonFromTree ( item . children )
240214 : [ ] ,
@@ -243,9 +217,6 @@ export function menuPropertyView(itemsComp: NavListCompType) {
243217 } ;
244218
245219 const newJson = buildJsonFromTree ( newItems ) ;
246-
247- // Use setChildrensAction for atomic update instead of delete-all/add-all
248- // This is more efficient and prevents UI glitches from multiple re-renders
249220 itemsComp . dispatch ( itemsComp . setChildrensAction ( newJson ) ) ;
250221 } ;
251222
0 commit comments