Adding Submenu to antd menu using map Loop - reactjs

let navigationConfig = [
{
key: "1",
path: `${match.url}/lead-information`,
title: "lead-information",
breadcrumb: true,
icon: HomeOutlined,
},
{
key: "2",
navigate: false,
breadcrumb: true,
onclick: () => editClickedReminder(),
title: "Add Reminder",
icon: BellOutlined,
},
{
key: "3",
title: "History",
icon: FieldTimeOutlined,
breadcrumb: true,
submenu: [
{
key: "4",
path: `${match.url}/edit`,
title: "History",
icon: EditFilled,
breadcrumb: true,
},
],
}
];
I have this code and i have mad a loop of navigation config and i have mentioned submenu in the navigation config but i am not getting submenu
{navigationConfig.map((menu) => {
return (
<Menu.Item key={menu.key} onClick={menu.onclick}>
{menu.icon ? <Icon type={menu?.icon} /> : null}
{menu.title}
{menu.path ? (
<Link
to={{
pathname: menu.path,
state: history.location.state,
}}
onClick={() => {
setHeaderTitle(
menu.hasOwnProperty("aliasTitle")
? menu.aliasTitle
: menu.title
);
}}
/>
) : null}
</Menu.Item>
);
})}
Does any one have have any idea how do i get submenu
Below is my screen shot of what i am getting

You should check this page https://ant.design/components/menu/
Did you try children instead of submenu ?

antd has two syntaxes for menus, the old one is by using this syntax
<Menu>
<Menu.Item>item 1</Menu.Item>
<Menu.Item>item 2</Menu.Item>
<Menu.SubMenu title="sub menu">
<Menu.Item>item 3</Menu.Item>
</Menu.SubMenu>
</Menu>
and the new syntax is like <Menu items={items} />
with the source data (which is named items here) format as below:
const items = [
{ label: 'item 1', key: 'item-1' }, // remember to pass the key prop
{ label: 'item 2', key: 'item-2' }, // which is required
{
label: 'sub menu',
key: 'submenu',
children: [{ label: 'item 3', key: 'submenu-item-1' }],
},
]
you seem to be mixing these two!
you could do one of the:
manually maping on data and returning the desired jsx element
changing the source data to correct format

Related

how can we have different functions on ANt design menu items in the new version?

I'm sure if you're using ant design menu component you've got the same error:
Warning: [antd: Menu] children will be removed in next major version. Please use items instead.
the new way to make this is:
const items = [
{ label: 'item 1', key: 'item-1' },
{ label: 'item 2', key: 'item-2' },
{
label: 'sub menu',
key: 'submenu',
children: [{ label: 'item 3', key: 'submenu-item-1' }],
},
];
return <Menu items={items} />
and you can define your function and use it like this:
return <Menu onClick={onClick} selectedKeys={[current]} mode="horizontal" items={items} />;
how can i have different functions on my items?
i'll be glad if someone help me with this
in the old way i could easily define any functions i want on any items
Actually, we can add functions to the items array as well
const [current, setCurrent] = useState();
const func = () => {
setCurrent(e.key);
}
const func2 = () => {
setCurrent(e.key);
}
const items = [
{
label: 'item 1',
key: 'item-1'
onClick: func,
className: class1,
},
{
label: 'item 1',
key: 'item-1'
onClick: func2,
className: class2,
},,
{
label: 'sub menu',
key: 'submenu',
children: [{ label: 'item 3', key: 'submenu-item-1' }],
},
];
return <Menu selectedKeys={[current]} mode="horizontal" items={items} />;

Material ui TreeItems change style conditionally based

I'm using TreeView, TreeItem of material ui.
I'm trying to style conditionally each node (selected, hover, focus).
actually what I'm trying to do is that only node that not disabledButton (props that come from outside) will be in right color.
only node that selectable will be in active color (expand node not means that node is selectable).
something like this example
I did this example, but the behavior is really weird, sometimes the "child-4" looks like the style of disabledButton even I sent the right props, maybe because it nested props, sometimes the other nested not get the right colors of the hover, focus and selected..
link for example (with colors)
example with different colors
I want it to be in real colors also with the theme (active should be like blue color, hover gray) so I created this example.
example with theme colors
sometimes it works as expected and sometimes is not.
I looked in this issue but it's not like my example but there it uses in classes but I don't want to send this way
After a little while this is what I got. I tried to match the design of the gif as good as possible.
I used this data which I derived from the question. This example has one disabled node and parent node (my-photo.jpg and zip directory). The data array can be expanded endlessly if it matches TreeData type.
export type TreeData = {
id: string;
name: string;
disabledButton: boolean;
children?: TreeData[];
};
const data: TreeData[] = [
{
id: "1",
name: "My Web Site",
disabledButton: false,
children: [
{
id: "2",
name: "images",
disabledButton: false,
children: [
{ id: "3", name: "logo.png", disabledButton: false },
{ id: "4", name: "body-back.png", disabledButton: false },
{ id: "5", name: "my-photo.jpg", disabledButton: true }
]
},
{
id: "6",
name: "resources",
disabledButton: false,
children: [
{
id: "7",
name: "pdf",
disabledButton: false,
children: [
{ id: "8", name: "brochure.pdf", disabledButton: false },
{ id: "9", name: "prices.pdf", disabledButton: false }
]
},
{
id: "10",
name: "zip",
disabledButton: true,
children: [{ id: "11", name: "test.zip", disabledButton: false }]
}
]
}
]
}
];
The CustomTreeView consists of CustomTreeItem that again makes use of CustomContent.
CustomContent is used to handle all events and to display another icon (folder) next to the expandIcon.
In the CustomTreeItem I set the width to fit-content to not select the whole row, but to match the example from the gif:
const CustomTreeItem = (props: TreeItemProps) => (
<TreeItem
ContentComponent={CustomContent}
{...props}
sx={{
width: "fit-content",
".MuiTreeItem-content": {
py: "2px",
width: "fit-content"
}
}}
/>
);
The interesting part is the styling of the CustomTreeView and its usage.
I have packed the classes into objects, which can be overwritten easily. The theming happens in the styles class which is in styles/Styles.ts.
// Styles.ts
import { createTheme } from "#mui/material";
export const getMuiTheme = () =>
createTheme({
palette: {
primary: {
main: "#2160dd"
},
secondary: {
main: "#d3d3d3"
}
}
});
// CustomTreeView.tsx
const classes = {
focused: {
bgcolor: "transparent",
py: "1px",
px: "7px",
border: `1px solid ${getMuiTheme().palette.secondary.main}`
},
selected: {
bgcolor: getMuiTheme().palette.primary.main,
color: "white"
}
};
const CustomTreeView = ({ data }: { data: TreeData[] }) => {
return (
<Box mt={2} ml={2} bgcolor="white" width="300px">
<ThemeProvider theme={getMuiTheme()}>
<TreeView
defaultCollapseIcon={<ArrowDropDownIcon />}
defaultExpandIcon={<ArrowRightIcon />}
defaultEndIcon={<InsertDriveFile />}
sx={{
".MuiTreeItem-root": {
".Mui-focused:not(.Mui-selected)": classes.focused,
".Mui-selected, .Mui-focused.Mui-selected, .Mui-selected:hover":
classes.selected
}
}}
>
{renderTreeData(data)}
</TreeView>
</ThemeProvider>
</Box>
);
};
To render arbitrarily nested data we make use of the recursive method renderTreeData. This way the data array can be expanded endlessly as long as it matches TreeData type.
export const renderTreeData = (data: TreeData[]) => {
return data.map((item) => (
<React.Fragment key={item.id}>
{item.children ? (
<CustomTreeItem
nodeId={item.id}
label={item.name}
disabled={item.disabledButton}
icon={<FolderOutlined />}
>
{renderTreeData(item.children)}
</CustomTreeItem>
) : (
<CustomTreeItem
nodeId={item.id}
label={item.name}
disabled={item.disabledButton}
icon={getFileIcon(item.name)}
/>
)}
</React.Fragment>
));
};
Live Demo

Rendering Material-UI icons from an array

Background info:
I'm using react and material-ui.
To keep the code clean, I populate menu items from a const array, like so:
const menuItems = [
{ label: "Home", path: "/home" },
{ label: "Accounts", path: "/accounts" },
{ label: "Organizations", path: "/organizations" },
];
Each item in the array is an object containing a label and a redirect path. I map over the items when rendering. Very basic.
Problem:
I would like to include a material-ui icon component in the menuItems array so the icon can be rendered next to the label. But I can't find a way to reference the icons by a name string
https://material-ui.com/components/material-icons/
Possible solutions:
put the icon component into a string:
{ label: "Accounts", path: "/accounts" }, icon: "<AccountBox/>"} but then I somehow need to evaluate the string into jsx. I don't know how.
Make a react functional component which renders a different icon depending on a prop, for example: <IconSwitch icon = {"accountIcon"} /> and hard-code different icons inside the RFC. Not pretty, but should work.
Punt and use different icons such as svg icons or font icons that can referenced by a name string.
Any suggestions on how to do this?
Thanks
Icon Font
You can use the Icon component. https://material-ui.com/components/icons/#icon-font-icons
To use an icon simply wrap the icon name (font ligature) with the Icon component, for example:
import Icon from '#material-ui/core/Icon';
<Icon>star</Icon>
https://codesandbox.io/s/material-demo-forked-sj66h?file=/demo.tsx
Assuming you set up your menu items with the appropriate icon ligatures:
const menuItems = [
{ label: "Home", path: "/home", icon: "home" },
{ label: "Accounts", path: "/accounts", icon: "account_circle" },
{ label: "Organizations", path: "/organizations", icon: "settings" }
];
Then you can map over them:
{menuItems.map(({ label, icon }) => {
return (
<span key={label}>
{label} <Icon>{icon}</Icon>
</span>
);
})}
SVG Icons
If you want to use SVG icons instead of basic icons, I'd recommend pulling only the SVG icons you plan to use in order to allow the icons you aren't using to be tree-shaken from the resulting bundle. The ability to tree shake is a good reason to use SVG icons over font icons.
import { Home, AccountCircle, Settings, AddCircle } from "#material-ui/icons";
If you want to allow user input of all icons or aren't aware ahead of time which icons will be displayed, you can import everything from #material-ui/icons as in Jonathan's answer.
If you aren't putting the list of icons into something that needs to be able to be stringified (i.e. Redux/sent through an API call) then you can just directly put the icons into the array and render them:
const menuItems: MenuItem[] = [
{ label: "Home", path: "/home", icon: <Home /> },
{ label: "Accounts", path: "/accounts", icon: <AccountCircle /> },
{ label: "Organizations", path: "/organizations", icon: <Settings /> }
];
// Rendering:
{menuItems.map(({ label, icon }) => {
return (
<span key={label}>
{label} {icon}
</span>
);
})}
If you are going to put the Icons somewhere that needs to be stringified, the above won't work, so I'd recommend putting the icons you want to use into an object to map them. That way you have a string to icon map.
Example: https://codesandbox.io/s/material-icons-svg-udcv3?file=/demo.tsx
import { Home, AccountCircle, Settings, AddCircle } from "#material-ui/icons";
const icons = {
Home,
AccountCircle,
Settings
};
In the case of the example above (i.e. rendering the icons from an array)
interface MenuItem {
label: string;
path: string;
icon: keyof typeof icons;
}
const menuItems: MenuItem[] = [
{ label: "Home", path: "/home", icon: "Home" },
{ label: "Accounts", path: "/accounts", icon: "AccountCircle" },
{ label: "Organizations", path: "/organizations", icon: "Settings" }
];
// Rendering:
{menuItems.map(({ label, icon }) => {
const Icon = icons[icon];
return (
<span key={label}>
{label} <Icon />
</span>
);
})}
You can import all from #material-ui/icons and than create an Icon component dynamically:
import React from 'react'
import * as icons from '#material-ui/icons'
interface MenuItem {
label: string,
icon: keyof typeof icons,
path: string
}
export function Menu() {
const menuItems: MenuItem[] = [
{ label: 'Home', path: './home', icon: 'Home' },
{ label: 'Accounts', path: './accounts', icon: 'AccountCircle' },
{ label: 'Organizations', path: './organizations', icon: 'Settings' }
]
return (
<>
{menuItems.map(menuItem => {
const Icon = icons[menuItem.icon]
return (
<span key={menuItem.path}>
{menuItem.label} <Icon />
</span>
)
})}
</>
)
}
// I have better way to avoid all of this other hustle .
// 1: Make Every icon in Array which is in Jsx from to simple name.
// Ex:
[
{ Name: "New", Icon: <HomeIcon /> },
{ Name: "JS Mastery", Icon: <CodeIcon /> },
{ Name: "Coding", Icon: <CodeIcon /> },
{ Name: "ReactJS", Icon: <CodeIcon /> },
{ Name: "NextJS", Icon: <CodeIcon /> },
]
to
[
({ Name: "New", Icon: HomeIcon },
{ Name: "JS Mastery", Icon: CodeIcon },
{ Name: "Coding", Icon: CodeIcon },
{ Name: "ReactJS", Icon: CodeIcon })
];
// 2: Remember Using Object keys name as capital ,here:- "Name , Icon" not "name , icon".
// 3: Now Simply use : -
{
categories.map(({ Name, Icon }) => (
<button key={Name}>
<span>{Name}</span>
<span> {<Icon/>} </span>
</button>
));
}
//use icon in this cleaver way

Mapping through Array with JSX

I am trying to map through a nested Array using JSX.
Here is the Array:
this.topics = [
{
id: 1,
name: 'first',
headings : [
{
id: 1,
url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
name: 'Sintel movie'
},
{
id: 2,
url: 'https://media.w3.org/2010/05/bunny/movie.mp4',
name: 'Bunny Movie'
},
{
id: 3,
url: 'https://techslides.com/demos/sample-videos/small.mp4',
name: 'Test page'
}
]
},
{
id: 2,
name: 'second',
headings : [
{
id: 1,
url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
name: 'Siddntel movie'
},
{
id: 2,
url: 'https://media.w3.org/2010/05/bunny/movie.mp4',
name: 'Bunnddy Movie'
},
{
id: 3,
url: 'https://techslides.com/demos/sample-videos/small.mp4',
name: 'Test ddpage'
}
]
}
];
And the JSX code that I have come up with so far:
renderSidenav(){
return(
<Nav>
{this.topics.map(topic =>
<Dropdown eventKey="3" title="s" icon={<Icon icon="magic" />}>
{this.topics[topic].headings.map(heading =>
<div onClick = {() => this.handleSelect(heading.id)} key={heading.id}>
<Dropdown.Item style={{backgroundColor: '#E9F4E4'}} icon={<Icon icon="dashboard"/>}>
<div>{heading.name}</div>
</Dropdown.Item>
<Dropdown.Item divider style={{backgroundColor: 'white', height: '2px'}}/>
</div>
)}
</Dropdown>
)}
</Nav>
)
}
And this is the error I get:
TypeError: Cannot read property 'headings' of undefined
What am I doing wrong?
Instead of this.topics[topic].headings.map(...) use topic.headings.map(...)
This is because .map() returns an object, and trying to use it as an index via topics[topic] will give you undefined.
Just do topic.headings.map instead of this.topics[topic].headings.map.
Map function returns the item, not the position/index in the array, therefore you can call it directly from topic.

How to show selected item in list with a different color in react native using native base ui kit?

I want to show the sidebar content in a different background color.For that I've tried TouchableOpacity underlay.But that is not the one I'm looking for.After giving TouchableOpacity ,it will change the color of the text only not the entire list background.How do I change the listitem background color as I'm using native base ui kit.Please help.Is there any method to do that?This is how the sidebar looks like.I've done something likes the following.Setting pressStatus as true within onPresList and if it is true change backround color.But navigation to route is not working.There is a mistake
https://i.stack.imgur.com/w9YiR.png
How do I change background color onPress? Following is my code.
updated
import React, { Component } from "react";
import { Image, FlatList } from "react-native";
import {
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
Thumbnail
} from "native-base";
import styles from "./style";
const drawerCover = require("../../imgs/quwait.jpg");
const datas = [
{
name: "Dashboard",
route: "Anatomy",
icon: require("../../imgs/dashboard.png"),
},
{
name: "Companies",
route: "Header",
icon: require("../../imgs/enterprise1.png"),
},
{
name: "Company Admin",
route: "Footer",
icon: require("../../imgs/icon1.png"),
},
{
name: "Employee",
route: "NHBadge",
icon: require("../../imgs/businessman1.png"),
},
{
name: "Employs",
route: "NHButton",
icon: require("../../imgs/employee1.png"),
},
{
name: "Announcement",
route: "NHCard",
icon: require("../../imgs/megaphone1.png"),
},
{
name: "Holiday",
route: "Check",
icon: require("../../imgs/sun-umbrella1.png"),
},
{
name: "Accounting Report",
route: "NHTypography",
icon: require("../../imgs/accounting1.png"),
},
{
name: "Invoice",
route: "NHCheckbox",
icon: require('../../imgs/approve-invoice1.png'),
},
{
name: "Settings",
route: "NHDatePicker",
icon: require('../../imgs/settings1.png'),
},
{
name: "Safety Phone Numbers",
route: "NHThumbnail",
icon: "user",
},
{
name: "NBK",
route: "NHDeckSwiper",
icon: "swap",
},
{
name: "ABK",
route: "NHFab",
icon: "help-buoy",
},
{
name: "CBK",
route: "NHForm",
icon: "call",
},
{
name: "Daily Invoice",
route: "NHIcon",
icon: "information-circle",
},
{
name: "Kolin",
route: "NHLayout",
icon: "grid",
},
{
name: "Limak",
route: "NHList",
icon: "lock",
},
{
name: "Polaytol",
route: "ListSwipe",
icon: "code-working",
},
{
name: "ACTS",
route: "NHPicker",
icon: "arrow-dropdown",
}
];
class SideBar extends Component {
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
pressStatus:false
};
}
onPressList = (DATA, INDEX) => {
this.props.navigation.navigate(DATA.route);
this.setState({ pressStatus : true, selectedItem: INDEX});
}
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff", top: -1 }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<FlatList
data={datas}
keyExtractor={(item, index) => String(index)}
renderItem={({ DATA, INDEX }) => {
<ListItem
button
noBorder
onPress={() => this.onPressList(DATA, INDEX)}
style={{
backgroundColor:
this.state.selectedItem === INDEX ? "#cde1f9" : "transparent"
}}
>
<Left>
<Image
source={DATA.icon }
style={{width:30,height:30}}
/>
<Text style={styles.text}>
{DATA.name}
</Text>
</Left>
</ListItem>}}
/>
</Content>
</Container>
);
}
}
export default SideBar;
In the App example from native Base they don't support styles for background items list. So you should change your List component from NativeBase and add a FlatList Component from react native. But you should also return the ListItem component from NativeBase and don't forget the import { FlatList } from "react-native";
You should also modify the onPressList function (I would transform it into an arrow function)
In your states you need to add the state selectedItem: 0
Everytime you press an item, your function would be called by modifying a selectedItem idex, which tells the Flatlist, which Item should get which background. I think this has to be the solution.
If it doesn't compile, make sure that you support arrow functions and that any curly braces or something like that isn't missing.
Final Code UPDATE
import React, { Component } from "react";
import { Image, FlatList } from "react-native";
import {
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
Thumbnail
} from "native-base";
import styles from "./style";
const drawerCover = require("../../imgs/quwait.jpg");
const datas = [
{
name: "Dashboard",
route: "Anatomy",
icon: require("../../imgs/dashboard.png"),
},
{
name: "Companies",
route: "Header",
icon: require("../../imgs/enterprise1.png"),
},
{
name: "Company Admin",
route: "Footer",
icon: require("../../imgs/icon1.png"),
},
{
name: "Employee",
route: "NHBadge",
icon: require("../../imgs/businessman1.png"),
},
{
name: "Employs",
route: "NHButton",
icon: require("../../imgs/employee1.png"),
},
{
name: "Announcement",
route: "NHCard",
icon: require("../../imgs/megaphone1.png"),
},
{
name: "Holiday",
route: "Check",
icon: require("../../imgs/sun-umbrella1.png"),
},
{
name: "Accounting Report",
route: "NHTypography",
icon: require("../../imgs/accounting1.png"),
},
{
name: "Invoice",
route: "NHCheckbox",
icon: require('../../imgs/approve-invoice1.png'),
},
{
name: "Settings",
route: "NHDatePicker",
icon: require('../../imgs/settings1.png'),
},
{
name: "Safety Phone Numbers",
route: "NHThumbnail",
icon: "user",
},
{
name: "NBK",
route: "NHDeckSwiper",
icon: "swap",
},
{
name: "ABK",
route: "NHFab",
icon: "help-buoy",
},
{
name: "CBK",
route: "NHForm",
icon: "call",
},
{
name: "Daily Invoice",
route: "NHIcon",
icon: "information-circle",
},
{
name: "Kolin",
route: "NHLayout",
icon: "grid",
},
{
name: "Limak",
route: "NHList",
icon: "lock",
},
{
name: "Polaytol",
route: "ListSwipe",
icon: "code-working",
},
{
name: "ACTS",
route: "NHPicker",
icon: "arrow-dropdown",
}
];
class SideBar extends Component {
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
pressStatus:false,
selectedItem:0
};
}
onPressList = (data, index) => {
this.props.navigation.navigate(data.route);
this.setState({ pressStatus : true, selectedItem: index});
}
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff", top: -1 }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<FlatList
data={datas}
keyExtractor={(item, index) => String(index)}
extraData={this.state.selectedItem}
renderItem={({item:data, index}) => {
const { selectedItem: sd } = this.state
const localColor ={backgroundColor: sd === index ? "#cde1f9" : "transparent"}
return (
<ListItem
button
noBorder
style={localColor}
onPress={() => this.onPressList(data, index)}
>
<Left>
<Image
source={data.icon}
style={{width:30,height:30}}
/>
<Text style={styles.text}>
{data.name}
</Text>
</Left>
</ListItem>
)
}}
/>
</Content>
</Container>
);
}
}
export default SideBar;

Resources