Skip to content

Commit 2b8873d

Browse files
committed
refactor: BaseMenu render functional
1 parent c6275e8 commit 2b8873d

File tree

3 files changed

+108
-22
lines changed

3 files changed

+108
-22
lines changed

src/SiderMenu/BaseMenu.tsx

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ export interface BaseMenuProps extends Partial<PureSettings>, PrivateSiderMenuPr
4242
}
4343

4444
// vue props
45-
export const VueBaseMenuProps = {
45+
export const baseMenuProps = {
4646
locale: Boolean,
47+
i18n: {
48+
type: Function as PropType<FormatMessage>,
49+
default: (t: string): string => t,
50+
},
4751
menuData: Array as PropType<MenuDataItem[]>,
4852
// top-nav-header: horizontal
4953
mode: {
@@ -131,42 +135,118 @@ const IconFont = createFromIconfontCN({
131135
scriptUrl: defaultSettings.iconfontUrl,
132136
});
133137

134-
const LazyIcon = (props: any) => {
135-
const { icon, prefixCls } = props;
138+
const LazyIcon = (props: {
139+
icon: VNode | string;
140+
iconPrefixes?: string;
141+
prefixCls?: string;
142+
}) => {
143+
const { icon, iconPrefixes = 'icon-', prefixCls = 'ant-pro' } = props;
136144
if (!icon) {
137145
return null;
138146
}
139147
if (typeof icon === 'string' && icon !== '') {
140148
if (isUrl(icon) || isImg(icon)) {
141149
return <img src={icon} alt="icon" class={`${prefixCls}-sider-menu-icon`} />;
142150
}
143-
if (icon.startsWith('icon-')) {
151+
if (icon.startsWith(iconPrefixes)) {
144152
return <IconFont type={icon} />;
145153
}
146154
}
147155
if (isVNode(icon)) {
148156
return icon;
149157
}
150-
const LazyIcon = resolveComponent(icon) as any;
151-
return (typeof LazyIcon === 'function' && <LazyIcon />) || null;
158+
const DynamicIcon = resolveComponent(icon) as any;
159+
return (typeof LazyIcon === 'function' && <DynamicIcon />) || null;
152160
};
153161

154-
LazyIcon.icon = {
155-
type: [String, Function, Object] as PropType<string | Function | VNodeChild | JSX.Element>,
162+
LazyIcon.props = {
163+
icon: {
164+
type: [String, Function, Object] as PropType<string | Function | VNode | JSX.Element>,
165+
},
166+
iconPrefixes: String,
167+
prefixCls: String,
156168
};
157169

170+
class MenuUtil {
171+
props: BaseMenuProps;
172+
173+
constructor(props: BaseMenuProps) {
174+
this.props = props;
175+
}
176+
177+
getNavMenuItems = (menusData: MenuDataItem[] = [], isChildren: boolean) => {
178+
return menusData.map(item => this.getSubMenuOrItem(item, isChildren)).filter(item => item);
179+
}
180+
181+
getSubMenuOrItem = (item: MenuDataItem, isChildren: boolean) => {
182+
if (Array.isArray(item.children)
183+
&& item.children.length > 0
184+
&& !item?.meta?.hideInMenu) {
185+
const { prefixCls, i18n } = this.props;
186+
const menuTitle = i18n && i18n(item.meta?.title) || item.meta?.title;
187+
const defaultTitle = item?.meta.icon ? (
188+
<span class={`${prefixCls}-menu-item`}>
189+
{!isChildren && <LazyIcon icon={item.meta.icon} />}
190+
<span class={`${prefixCls}-menu-item-title`}>{menuTitle}</span>
191+
</span>
192+
) : (
193+
<span class={`${prefixCls}-menu-item`}>{menuTitle}</span>
194+
);
195+
const MenuComponent = item.meta?.type === 'group' ? Menu.ItemGroup : Menu.SubMenu;
196+
return (
197+
<MenuComponent title={defaultTitle} key={item.path}>
198+
{this.getNavMenuItems(item.children, true)}
199+
</MenuComponent>
200+
);
201+
}
202+
203+
return (
204+
<Menu.Item
205+
inlineIndent={24}
206+
disabled={item.meta?.disabled}
207+
key={item.path}
208+
// onClick={}
209+
>
210+
{this.getMenuItem(item, isChildren)}
211+
</Menu.Item>
212+
);
213+
}
214+
215+
getMenuItem = (item: MenuDataItem, isChildren: boolean) => {
216+
const meta = Object.assign({}, item.meta);
217+
const target = meta.target || null;
218+
const hasUrl = isUrl(item.path);
219+
const CustomTag: any = resolveComponent((target && 'a') || 'router-link');
220+
const props = { to: { name: item.name } };
221+
const attrs = hasUrl || target ? { href: item.path, target: target } : {};
222+
223+
const { prefixCls, i18n } = this.props;
224+
const menuTitle = i18n && i18n(item.meta?.title) || item.meta?.title;
225+
const defaultTitle = item?.meta.icon ? (
226+
<span class={`${prefixCls}-menu-item`}>
227+
<CustomTag {...attrs} {...props}>
228+
{!isChildren && <LazyIcon icon={item.meta.icon} />}
229+
<span class={`${prefixCls}-menu-item-title`}>{menuTitle}</span>
230+
</CustomTag>
231+
</span>
232+
) : (
233+
<span class={`${prefixCls}-menu-item`}>{menuTitle}</span>
234+
);
235+
236+
return defaultTitle;
237+
}
238+
239+
conversionPath = (path: string) => {
240+
if (path && path.indexOf('http') === 0) {
241+
return path;
242+
}
243+
return `/${path || ''}`.replace(/\/+/g, '/');
244+
};
245+
}
246+
158247
export default defineComponent({
159248
name: 'BaseMenu',
160-
props: Object.assign(
161-
{},
162-
{
163-
i18n: {
164-
type: Function as PropType<FormatMessage>,
165-
default: (t: string): string => t,
166-
},
167-
},
168-
VueBaseMenuProps,
169-
),
249+
props: baseMenuProps,
170250
emits: ['update:openKeys', 'update:selectedKeys'],
171251
setup(props, { emit }) {
172252
const { mode, i18n } = toRefs(props);
@@ -184,6 +264,8 @@ export default defineComponent({
184264
}): void => {
185265
emit('update:selectedKeys', params.selectedKeys);
186266
};
267+
// TODO :: add `Menu` onClick custom handle.
268+
187269
return () => (
188270
<Menu
189271
key="Menu"

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export {
1818
MenuMode,
1919
OpenEventHandler,
2020
SelectInfo,
21-
VueBaseMenuProps,
21+
baseMenuProps,
2222
} from './SiderMenu/BaseMenu';
2323

2424
export { default } from './BasicLayout';

src/typings.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { VNodeChild } from 'vue';
1+
import { VNode } from 'vue';
22

33
// define global types
4-
export type RenderVNodeType = VNodeChild | Element | JSX.Element;
4+
export type RenderVNodeType = VNode | Element | JSX.Element;
55

66
export type MenuTheme = 'dark' | 'light';
77

@@ -15,7 +15,11 @@ export interface MetaRecord {
1515
/**
1616
* @name 菜单的icon
1717
*/
18-
icon?: string | VNodeChild | JSX.Element;
18+
icon?: string | VNode;
19+
/**
20+
* @type 有 children 的菜单的组件类型 可选值 'group'
21+
*/
22+
type?: string;
1923
/**
2024
* @name 自定义菜单的国际化 key,如果没有则返回自身
2125
*/

0 commit comments

Comments
 (0)