Repository: CNFeffery/feffery-antd-components (language: python)

Issue:
[BUG] `AntdMenu`组件`onlyExpandCurrentSubMenu`参数在多级菜单下表现异常



Discussion / hints:

Fix patch:
diff --git a/src/lib/components/navigation/AntdMenu.react.js b/src/lib/components/navigation/AntdMenu.react.js
index 86cb35fd..42ad0b83 100644
--- a/src/lib/components/navigation/AntdMenu.react.js
+++ b/src/lib/components/navigation/AntdMenu.react.js
@@ -1,5 +1,5 @@
 // react核心
-import React, { Component, useEffect } from 'react';
+import React, { Component, useEffect, useMemo } from 'react';
 import PropTypes from 'prop-types';
 // antd核心
 import { Menu, Button } from 'antd';
@@ -68,6 +68,22 @@ function findKeyPath(array, key, path = []) {
     return null;
 }
 
+const getLevelKeys = (items1) => {
+    const key = {};
+    const func = (items2, level = 1) => {
+        items2.forEach((item) => {
+            if (item?.props?.key) {
+                key[item.props.key] = level;
+            }
+            if (item?.children) {
+                func(item.children, level + 1);
+            }
+        });
+    };
+    func(items1);
+    return key;
+};
+
 class UtilsLink extends Component {
 
     constructor(props) {
@@ -133,9 +149,6 @@ const str2Jsx = new Map([
     ['Divider', Divider]
 ])
 
-// 当前的SubMenu节点key值数组
-const rootSubmenuKeys = [];
-
 // 递归推导多层菜单结构
 const raw2Jsx = (obj, str2Jsx, menuItemKeyToTitle) => {
     // 若obj为数组
@@ -148,7 +161,6 @@ const raw2Jsx = (obj, str2Jsx, menuItemKeyToTitle) => {
         if (obj.hasOwnProperty('children')) {
             Object.assign(obj, { children: obj.children.map(obj_ => raw2Jsx(obj_, str2Jsx, menuItemKeyToTitle)) })
             if (obj.component === 'SubMenu') {
-                rootSubmenuKeys.push(obj.props.key)
                 obj = <SubMenu
                     key={obj.props.key}
                     title={menuItemKeyToTitle[obj.props.key] || obj.props.title}
@@ -281,13 +293,27 @@ const AntdMenu = (props) => {
         loading_state
     } = props;
 
+    const levelKeys = useMemo(() => {
+        return getLevelKeys(menuItems);
+    }, [menuItems]);
+
     const onOpenChange = (keys) => {
         if (onlyExpandCurrentSubMenu) {
-            const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1);
-            if (latestOpenKey && rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
-                setProps({ openKeys: keys });
+            if (isUndefined(openKeys) || isNull(openKeys)) {
+                openKeys = [];
+            }
+            const currentOpenKey = keys.find((key) => openKeys.indexOf(key) === -1);
+            if (!isUndefined(currentOpenKey)) {
+                const repeatIndex = keys
+                    .filter((key) => key !== currentOpenKey)
+                    .findIndex((key) => levelKeys[key] === levelKeys[currentOpenKey]);
+                setProps({
+                    openKeys: keys
+                        .filter((_, index) => index !== repeatIndex)
+                        .filter((key) => levelKeys[key] <= levelKeys[currentOpenKey]),
+                });
             } else {
-                setProps({ openKeys: latestOpenKey ? [latestOpenKey] : [] });
+                setProps({ openKeys: keys });
             }
         } else {
             setProps({ openKeys: keys });


Test patch:
diff --git a/tests/navigation/AntdMenu/fix_onlyExpandCurrentSubMenu.py b/tests/navigation/AntdMenu/fix_onlyExpandCurrentSubMenu.py
new file mode 100644
index 00000000..1b8ba1d0
--- /dev/null
+++ b/tests/navigation/AntdMenu/fix_onlyExpandCurrentSubMenu.py
@@ -0,0 +1,57 @@
+if True:
+    import sys
+
+    sys.path.append('../../../')
+    from dash import Dash, html
+    import feffery_antd_components as fac
+    from feffery_dash_utils.style_utils import style
+
+app = Dash(__name__)
+
+app.layout = html.Div(
+    [
+        fac.AntdMenu(
+            id='menu-demo',
+            currentKey='1-1-1',
+            menuItems=[
+                {
+                    'component': 'SubMenu',
+                    'props': {
+                        'key': f'{sub_menu}',
+                        'title': f'子菜单{sub_menu}',
+                    },
+                    'children': [
+                        {
+                            'component': 'ItemGroup' if item_group == 1 else 'SubMenu',
+                            'props': {
+                                'key': f'{sub_menu}-{item_group}',
+                                'title': f'菜单项分组{sub_menu}-{item_group}',
+                            },
+                            'children': [
+                                {
+                                    'component': 'Item',
+                                    'props': {
+                                        'key': f'{sub_menu}-{item_group}-{item}',
+                                        'title': f'菜单项{sub_menu}-{item_group}-{item}',
+                                    },
+                                    'extra': f'extra-{sub_menu}-{item_group}-{item}',
+                                }
+                                for item in range(1, 4)
+                            ],
+                        }
+                        for item_group in range(1, 4)
+                    ],
+                }
+                for sub_menu in range(1, 5)
+            ],
+            mode='inline',
+            onlyExpandCurrentSubMenu=True,
+            style=style(width=256),
+        ),
+    ],
+    style=style(padding=50),
+)
+
+
+if __name__ == '__main__':
+    app.run(debug=True)