What I'm trying to do is get which is the key of the sider the user is using (without ReactDOM and with functional components)
my sample code here:
export default function DrawerSider() {
const history = useHistory();
const [selectedKey, setSelectedKey] = useState("sub1")
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
console.log(key)
}
return (
<Sider width={200} className="site-layout-background">
<Menu
defaultSelectedKeys={selectedKey}
mode="inline"
style={{ height: "100%", borderRight: 0 }}
>
<Menu.Item
key="sub1"
icon={<HomeOutlined />}
onClick={() => handleSelectKey("sub1","/dashboard/resumo")}
>
Dashboard
</Menu.Item>
<SubMenu key="sub2" icon={<UserOutlined />} title="Usuários">
<Menu.Item
key="1"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("1","/usuarios/novo")}
>
Adicionar usúario
</Menu.Item>
<Menu.Item
key="2"
icon={<TableOutlined />}
onClick={() => handleSelectKey("2","/usuarios/todos")}
>
Todos usúarios
</Menu.Item>
</SubMenu>
<SubMenu key="sub3" icon={<FormOutlined />} title="Formulários">
<Menu.Item
key="3"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("3","/formularios/novo")}
>
Adicionar formulário
</Menu.Item>
</SubMenu>
</SubMenu>
</Menu>
</Sider>
);
}
Obs: I'm using ant design lib
Someone knows how to make it works?
I tried to use a const on click event to set a state of the selected key , but it didn't work
There is no onClick on Menu.Item
only Menu component has onClick or onSelect, both will give you callback of the "clicked" key, the differences is onClick is for any menu item click (including expanding menu) while on select is when you select an actual menu item.
There are 2 ways to get the history string:
1 - use the history string as menuItem key:
the issue would be you cannot have 2 menu item that has same history key
2 - have a map which maps the menu key to history string
see below demo:
https://codesandbox.io/s/inline-menu-antd-4-18-2-forked-bgwyj?file=/index.js
const handleOnSelect = ({ item, key, keyPath, selectedKeys, domEvent }) => {
console.log(item);
console.log(keyPath);
console.log(selectedKeys);
console.log(domEvent);
handleSelectKey(key);
};
...
..
<Menu
onSelect={handleOnSelect}
...
>
...
</Menu>
You need to save the selected key as part of the component's state. You can set the current selected key as part of the onClick.
const selectedKey, setSelectedKey = useState("defaultSelectedKey")
Not sure what the useHistory hook is for but if you need to set that onClick as well as the selectedKey, move it all into one function like so
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
}
...
onclick={() => handleSelectedKey("3", "/usuarios/novo")}
https://ant.design/components/menu/
EDIT
Based on research into your design library, your onclick and handle select must be at the top level of the menu component. Just copy the syntax that they use in their example code.
https://ant.design/components/menu/
Related
I'm using the react-bootstrap library in a project, and have used the <Dropdown /> component, and I'm trying to get it to include a filterable search. I've got this from the docs:
import React, { useState } from 'react';
import Dropdown from 'react-bootstrap/Dropdown';
import Form from 'react-bootstrap/Form';
// The forwardRef is important!!
// Dropdown needs access to the DOM node in order to position the Menu
const CustomToggle = React.forwardRef(({ children, onClick }, ref) => (
<a
href=""
ref={ref}
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
>
{children}
▼
</a>
));
// forwardRef again here!
// Dropdown needs access to the DOM of the Menu to measure it
const CustomMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
const [value, setValue] = useState('');
return (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<Form.Control
autoFocus
className="mx-3 my-2 w-auto"
placeholder="Type to filter..."
onChange={(e) => setValue(e.target.value)}
value={value}
/>
<ul className="list-unstyled">
{React.Children.toArray(children).filter(
(child) =>
!value || child.props.children.toLowerCase().startsWith(value),
)}
</ul>
</div>
);
},
);
render(
<Dropdown>
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components">
Custom toggle
</Dropdown.Toggle>
<Dropdown.Menu as={CustomMenu}>
<Dropdown.Item eventKey="1">Red</Dropdown.Item>
<Dropdown.Item eventKey="2">Blue</Dropdown.Item>
<Dropdown.Item eventKey="3" active>
Orange
</Dropdown.Item>
<Dropdown.Item eventKey="1">Red-Orange</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>,
);
And with the default data it works, but if I replace it with some data which I map over:
<Dropdown className="mb-2">
<Dropdown.Toggle
className="form-control"
as={CustomToggle}
id="dropdown-custom-components"
>
Search for contact...
</Dropdown.Toggle>
<Dropdown.Menu as={CustomMenu}>
{allContacts.map((contact, key) => {
return (
<Dropdown.Item
key={key}
value={contact.name}
onClick={(e) => {
console.log(e);
setSelectedContact(contact);
}}
>
{contact.name} - {contact.outlet}
</Dropdown.Item>
);
})}
</Dropdown.Menu>
</Dropdown>
It displays the data in the dropdown but if I try and search for something I get an error: Objects are not valid as a React child (found: object with keys {map, forEach, count, toArray, only}). If you meant to render a collection of children, use an array instead. and this confused me because the data I'm mapping over (allContacts) is already an array, but it is an array of objects, if that makes a difference?
I'd appreciate any pointers, thank you!
Just in case anyone else has the same problem, I've managed to solve it for me.
If I don't pass the key prop in the map() function, and instead use the UID of the contact, and remove the value attribute, it then works!
I am very new in React and maybe someone could help me.
I want to toggle my Sidebar in my Header Component with Hooks.
This is my Code:
Sidebar.js
...imported things
...styles
export const SideBar = () => {
const history = useHistory();
return (
<StyledSideBar>
<Tooltip title="Dashboard" placement="right" arrow >
<StyledButton onClick={() => history.push('/dashboard')}>
<FontAwesomeIcon icon={faTachometerAltFast} />
</StyledButton>
</Tooltip >
<Tooltip title="Chat" placement="right" arrow>
<StyledButton onClick={() => history.push('/chat')}>
<FontAwesomeIcon icon={faCommentsAlt} />
</StyledButton>
</Tooltip>
<Tooltip title="Calendar" placement="right" arrow>
<StyledButton onClick={() => history.push('/calendar')}>
<FontAwesomeIcon icon={faCalendarAlt} />
</StyledButton>
</Tooltip>
</StyledSideBar>
);
}
export default SideBar;
Header.js
...imported things
...styles
import SideBar from '../sidebar';
export const MainHeader = () => {
const [show, setShow] = React.useState(false);
const toggle = () => setShow(!show);
return (
<AppBar elevation={2} position="sticky" color="inherit" >
<FlexToolbar variant="regular">
<StyledMenuIcon open={show} onClick={toggle.{SideBar}>
<FontAwesomeIcon icon={faBars} />
</StyledMenuIcon>
<Logo src="/assets/images/logo.svg" alt="Vetera Sky" />
<BreakpointLogo src="/assets/images/get-started-icon.svg" alt="Vetera Sky" />
<Spacer />
<LogoutButton onClick={onLogout}>
<FontAwesomeIcon icon={faPowerOff} />
<Typography variant="label" color={actionSecondary}>Logout</Typography>
</LogoutButton>
<BreakpointLogoutButton onClick={onLogout}>
<FontAwesomeIcon icon={faPowerOff} />
</BreakpointLogoutButton>
</FlexToolbar>
</AppBar>
)};
export default MainHeader;
I know this is wrong, but i could not find anything in the Web or in here.
If the StyledMenuIcon is clicked once, the Sidebar should open on the left and if clicked again it should close.
Hope someone can help me soon :)
You should move your state higher up in the tree where both MainHeader and SideBar components can use it:
function App() {
const [showSidebar, setShowSidebar] = useState(false);
const onToggleSidebar = () => {
setShowSidebar(!showSidebar);
};
return (
<MainHeader onToggleSidebar={onToggleSidebar} />
<SideBar open={showSidebar}
);
}
If the components aren't available on the same level in the tree, you could use React's context api to use this logic from different places down the tree.
Or if you're already using something like redux or another state management system you could move your app state there.
I want to implement two buttons Select All and Select None inside Autocomplete React Material UI along with checkbox for each option.When Select All button is clicked all the options must be checked and when I click Select None all the options must be unchecked.
How do I implement that ?
<Autocomplete
id={id }
size={size}
multiple={multiple}
value={value}
disabled={disabled}
options={items}
onChange={handleChange}
getOptionLabel={option => option.label}
renderOption={(option, { selected }) => (
<React.Fragment >
{isCheckBox(check, selected)}
{option.label}
</React.Fragment>
)}
renderInput={params => (
<TextField id="dropdown_input"
{...params} label="controlled" variant={variant} label={label} placeholder={placeholder} />
)}
/>
export function isCheckBox(check, selected) {
if (check) {
const CheckBox = <Checkbox
id="dropdown_check"
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
return CheckBox;
}
return null;
}
I stumbled into the same issue earlier today.
The trick is to use local state to manage what has been selected, and change the renderOption to select * checkboxes if the local state has the 'all' key in it.
NB: At the time of writing React 16 is what I'm working with
I'm on a deadline, so I'll leave a codesandbox solution for you instead of a rushed explanation. Hope it helps :
Select All AutoComplete Sandbox
Updated
for React version 16.13.1 and later. codesandbox
const [open, setOpen] = useState(false);
const timer = useRef(-1);
const setOpenByTimer = (isOpen) => {
clearTimeout(timer.current);
timer.current = window.setTimeout(() => {
setOpen(isOpen);
}, 200);
}
const MyPopper = function (props) {
const addAllClick = (e) => {
clearTimeout(timer.current);
console.log('Add All');
}
const clearClick = (e) => {
clearTimeout(timer.current);
console.log('Clear');
}
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
return (
<Autocomplete
PopperComponent={MyPopper}
onOpen={(e) => {
console.log('onOpen');
setOpenByTimer(true);
}}
onClose={(obj,reason) => {
console.log('onClose', reason);
setOpenByTimer(false);
}}
open={open}
.....
....
/>
);
Old Answer
Just customise PopperComponent and do whatever you want.
Autocomplete API
const addAllClick = (e: any) => {
setValue(items);
};
const clearClick = (e: any) => {
setValue([]);
};
const MyPopper = function (props: any) {
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
<Autocomplete
PopperComponent={MyPopper}
...
/>
If you want to make an autocomplete with select all option using react material ui and react hook form, you can implement to Autocomplete like so
multiple: To allow multiple selection
disableCloseOnSelect: To disable the close of the box after each selection
options: Array of items of selection
value: Selected options.
getOptionLabel: The string value of the option in our case is name
filterOptions: A function that determines the filtered options to be rendered on search, in our case we used it to add selectAll checkbox.
renderOption: Render the option, use getOptionLabel by default.
renderInput: To render the input,
onChange: Callback fired when the value changes
Now you can play with selected values using handleChange so once the select is fired check if the selected option is select all if yes then set the newest selectedOptions
<Autocomplete
multiple
disableCloseOnSelect
options={items}
value={selectedOptions}
getOptionLabel={(option) => option.name}
filterOptions={(options, params) => {
const filtered = filter(options, params)
return [{ id: 0, name: selectAllLabel }, ...filtered]
}}
renderOption={(props, option, { selected }) => {
// To control the state of 'select-all' checkbox
const selectAllProps =
option.name === 'Sélectionner Tous' ? { checked: allSelected } : {}
return (
<li {...props}>
<Checkbox checked={selected} {...selectAllProps} />
{option.name}
</li>
)
}}
renderInput={(params) => (
<TextField {...params} label={label} placeholder={label} />
)}
onChange={handleChange}
/>
you can refer to the Autocomplete API to get detailed definition of each item
You can refer to this codeSendBox to check a demo of react material Autocomplete with select all using react material ui version 5 and react hook form verion 7
folks
So my issue is simple. I want to provide custom closeIconButton. And only closeIcon prop is available.
CloseIcon prop doesn't suffice because I need this custom button to have onClick property.
And if I place onClick on CloseIcon material-ui will warn: Failed prop type: Material-UI: you are providing an onClick event listener to a child of a button element.
Firefox will never trigger the event.
<Autocomplete
open={open}
classes={classes}
options={practicesList}
getOptionLabel={get('name')}
value={curPractice}
blurOnSelect
closeIcon={<CloseIcon onClick={() => onChange(null)} />}
onChange={async (e, option) => {
if (!option) return
onChange(option.id)
}}
renderInput={params => <TextField {...params} autoFocus={autoFocus} fullWidth label={label} margin="none" />}
renderOption={(practice, { inputValue }) => {
const matches = match(practice.name, inputValue)
const letters = parse(practice.name, matches)
return (
<div>
{letters.map((letter, i) => (
<span key={i} style={{ fontWeight: letter.highlight ? 700 : 400 }}>
{letter.text}
</span>
))}
</div>
)
}}
/>
From api docs closeIcon should be just some node. For example:
<CloseIcon fontSize="small" />
Than, you can use onClose prop directly on Autocomplete component to specify some callback. Hope this helps.
<Menu tabular>
{
menus.map((menu, index) => (
<Menu.Item
key={index}
name={menu.name}
active={this.props.location.pathname === menu.path}
onClick={() => {this.props.handleOpenClick(menu)}}
>
{menu.name}
<Button onClick={() => {this.props.handleCloseClick(index)}}>X</Button>
</Menu.Item>
))
}
</Menu>
I want to create a dynamic tabular layout with closable feature by using Menu and tabular option. I put the Button component inside a Menu.Item and implement the onClick event.
When the buton is clicked, it's also calling handleOpenClick() function on the Menu.Item component. I need to call the handleCloseClick() only.
Is there any suggestion ?
Sorry for my bad english
Try adding e.stopPropagation().
<Menu tabular>
{
menus.map((menu, index) => (
<Menu.Item
key={index}
name={menu.name}
active={this.props.location.pathname === menu.path}
onClick={() => {this.props.handleOpenClick(menu)}}
>
{menu.name}
<Button onClick={e => {
e.stopPropagation();
this.props.handleCloseClick(index);
}}>X</Button>
</Menu.Item>
))
}
</Menu>