How can I put a button into collapse? - reactjs

I want to put a button into a collapse, I am using the collapse of antd, that new button doesn't should open or close the collapse, I want to give her other functionality?
const { Collapse, Button } = antd;
const { Panel } = Collapse;
function callback(key) {
console.log(key);
}
const text = ` hi `;
ReactDOM.render(
<Collapse
defaultActiveKey={['1']} onChange={callback}>
<Panel header={<Button type="primary">Primary Button</Button>} key="1" >
<Button type="link">My button</Button> >
<p>{text}</p>
</Panel>
</Collapse>,
mountNode,
);
Why does the COLLAPSE open when I click the button? I don't want the COLLAPSE to open

I guess the button you want to not open the collapse is your Primary Button in header of Panel, To do that you have to control activeKey manually, and check that when user click on the Panel header are they click on the Primary Button or outside of it.
Try this:
import React, { useState, useRef } from "react";
import * as antd from "antd";
const { Collapse, Button } = antd;
const { Panel } = Collapse;
const text = ` hi `;
export const App = () => {
const [activeKey, setActiveKey] = useState(0);
const isButtonClicked = useRef(false);
const callback = key => {
console.log(key, isButtonClicked.current);
// Check if use click on the button don not update activekey
if (
isButtonClicked &&
isButtonClicked.current &&
isButtonClicked.current === true
) {
isButtonClicked.current = false;
return;
}
if (!activeKey) {
setActiveKey(key);
} else {
setActiveKey(0);
}
};
return (
<Collapse activeKey={activeKey} onChange={callback}>
<Panel
header={
<Button
onClick={() => {
// set the isButtonClicked ref to tru when user click on Button
isButtonClicked.current = true;
// Do other functionality you want for this button here
}}
type="primary"
>
Primary Button
</Button>
}
key="1"
>
<Button type="link">My button</Button> ><p>{text}</p>
</Panel>
</Collapse>
);
};
I create a codesandbox to demonstrate it

try this:
const { Collapse, Button } = antd;
const { Panel } = Collapse;
function callback(key) {
console.log(key);
}
const text = ` hi `;
ReactDOM.render(
<Collapse defaultActiveKey={['1']} onChange={callback}>
<Panel header={<Button type="primary">Primary Button</Button>} key="1" >
<Button type="link">My button</Button>
<p>{text}</p>
</Panel>
</Collapse>,
mountNode,
);

If you want a button inside a panel, the button code should be inside the panel, not inside its tag.. instead of:
<Panel header="This is panel header 1" key="1"
<Button type="link">My button</Button> >
<p>{text}</p>
</Panel>
this should be
<Panel header="This is panel header 1" key="1">
<Button type="link">My button</Button>
<p>{text}</p>
</Panel>

Related

Shared Popper requires two clicks to reopen

I've got buttons that represent sports. When a user clicks on a button a Popper is opened with the sport's associated component/content. The same Popper is shared between all the buttons.
This works fine, but once the Popper is open it requires two clicks to open the Popper for another sport. One click to close the Popper and the second click to open it. So if I click the 'Baseball' button, I'll have to click the 'Basketball' button twice before the Popper is reopened with the Basketball content.
Is there any way to accomplish this with just a single click? Link to Sandbox
import React, { useEffect, useState } from 'react';
import { Popper, Button, Paper, Typography } from "#material-ui/core";
function CategoryMenuItemContent(props) {
return (
<>
<p>{props.menu.label}</p>
<p>{props.menu.content}</p>
</>
);
}
export default function App() {
const baseballCategory = {
label: 'Baseball',
content: <p>some content</p>
};
const basketballCategory = {
label: 'Basketball',
content: <p>some content</p>
};
const footballCategory = {
label: 'Football',
content: <p>some content</p>
};
const hockeyCategory = {
label: 'Hockey',
content: <p>some content</p>
};
const [activeCategoryMenuContent, setActiveCategoryMenuContent] = React.useState('baseball');
const categoryMenuItemContentComponents = {
"baseball": <CategoryMenuItemContent menu={baseballCategory} />,
"basketball": <CategoryMenuItemContent menu={basketballCategory} />,
"football": <CategoryMenuItemContent menu={footballCategory} />,
"hockey": <CategoryMenuItemContent menu={hockeyCategory} />,
};
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
setActiveCategoryMenuContent(event.currentTarget.textContent.toLowerCase());
};
const open = Boolean(anchorEl);
const id = open ? 'simple-popper' : undefined;
return (
<>
<Button onClick={(event) => {handleClick(event);}}>Baseball</Button>
<Button onClick={(event) => {handleClick(event);}}>Basketball</Button>
<Button onClick={(event) => {handleClick(event);}}>Football</Button>
<Button onClick={(event) => {handleClick(event);}}>Hockey</Button>
<Popper id={id} open={open} anchorEl={anchorEl}>
{activeCategoryMenuContent === 'baseball' && categoryMenuItemContentComponents['baseball']}
{activeCategoryMenuContent === 'basketball' && categoryMenuItemContentComponents['basketball']}
{activeCategoryMenuContent === 'football' && categoryMenuItemContentComponents['football']}
{activeCategoryMenuContent === 'hockey' && categoryMenuItemContentComponents['hockey']}
</Popper>
</>
);
}
This would make more sense:
const handleClick = (event) => {
if (anchorEl === event.currentTarget) {
setAnchorEl(null)
setActiveCategoryMenuContent('')
} else {
setAnchorEl(event.currentTarget);
setActiveCategoryMenuContent(event.currentTarget.textContent.toLowerCase())
}
};

Possible to add css styles to specific component buttons?

I'm trying to add a white outline via css box-shadow, but whenever I click on any of the buttons, they all get the outline instead of just the actual button I clicked.
Is there a way so only the button component I click on gets the outline and then toggles off if I click it again?
Here is my current code:
const [selectState, setSelectState] = useState(false);
const Button = ({ selected, text }) => {
function handleClick() {
setSelectState(true);
}
return (
<span
onClick={handleClick}
className={`btn-style ${selected ? "selected" : ""}`}
>
{text}
</span>
);
};
export default function Hello() {
return (
<Button selected={selectState} text='Blue'/>
<Button selected={selectState} text='Red'/>
<Button selected={selectState} text='Green'/>
);
}
.selected css:
.selected {
box-shadow: rgb(17 206 101) 0px 0px 0px 2px inset !important;
}
If you want to track the selected state of individual elements, you'd need to handle the onClick method and make corresponding state change in parent element.
const Button = ({ selected, text, onClick }) => {
return (
<span
onClick={onClick}
className={`btn-style ${selected ? "selected" : ""}`}
>
{text}
</span>
);
};
export default function Hello() {
const [selectState, setSelectState] = React.useState(0);
return (
<React.Fragment>
<Button
onClick={() => setSelectState(1)}
selected={selectState === 1}
text="Blue"
/>
<Button
onClick={() => setSelectState(2)}
selected={selectState === 2}
text="Red"
/>
<Button
onClick={() => setSelectState(3)}
selected={selectState === 3}
text="Green"
/>
</React.Fragment>
);
}
You can have the click handler tell the parent component to save the clicked button index in state, and pass that state down to determine whether the selected class is needed:
const Button = ({ selected, text, onClick }) => {
return (
<span
onClick={onClick}
className={`btn-style ${selected ? "selected" : ""}`}
>
{text}
</span>
);
};
export default function Hello({ texts }) {
const [selectedIndex, setSelectedIndex] = useState(-1);
return (<>
{
texts.map((text, i) => <Button selected={i === selectedIndex} text={text} onClick={() => setSelectedIndex(i)}} />)
}
</>);
}
ReactDOM.render(<Hello texts={['Blue', 'Red', 'Green']} />, document.querySelector('.react'));

GatsbyJS Burger Menu close menu on AnchorLink click

I have created a working burger menu in my Gatsby application using react-burger-menu. I am trying to get it so that when an AnchorLink in the burger is clicked, the Burger menu closes. I have tried doing <Menu isOpen={ false }> as suggested in the docs but not working. Any help?
This is my Burger menu code:
import React from "react"
import { slide as Menu } from "react-burger-menu"
import { AnchorLink } from "gatsby-plugin-anchor-links"
const Burger = () => {
return (
<div className="burger-menu">
<Menu right isOpen={ false }>
<AnchorLink to="/#about">About Me</AnchorLink>
<AnchorLink to="/#experience">Experience</AnchorLink>
<AnchorLink to="/#projects">Projects</AnchorLink>
<AnchorLink to="/#contact">Contact</AnchorLink>
</Menu>
</div>
)
}
export default Burger
You can provide event handlers for Menu and AnchorLink and add a state menuOpen to close/open sidebar.
const Burger = () => {
const [menuOpen, setMenuOpen] = React.useState(false)
// This keeps your state in sync with the opening/closing of the menu
// via the default means, e.g. clicking the X, pressing the ESC key etc.
const handleStateChange = state => {
setMenuOpen(state.isOpen)
}
// This can be used to close the menu, e.g. when a user clicks a menu item
const closeMenu = () => {
setMenuOpen(false)
}
return (
<div className="burger-menu">
<Menu
right
isOpen={menuOpen}
onStateChange={state => handleStateChange(state)}
>
<AnchorLink
to="/#about"
onClick={() => {
closeMenu()
}}
>
About Me
</AnchorLink>
<AnchorLink
to="/#experience"
onClick={() => {
closeMenu()
}}
>
Experience
</AnchorLink>
<AnchorLink
to="/#projects"
onClick={() => {
closeMenu()
}}
>
Projects
</AnchorLink>
<AnchorLink
to="/#contact"
onClick={() => {
closeMenu()
}}
>
Contact
</AnchorLink>
</Menu>
</div>
)
}

How to move the cursor to the end of the line?

I am using draft.js to develop a Rich text editor. I want the user to be able to keep typing once the Italic button is clicked. And inline styling should be applied until the user disable the italic button. Clicking on the button make the cursor to focus out of the editor. I created a ref and called the focus() function on the current ref and then called moveFocusToEnd on on edotorState. This does not work as expected. How do I achieve this behavior?
ReactJS
import React from 'react';
import { Editor, EditorState, RichUtils } from 'draft-js';
import { Button, Icon } from 'antd';
function MyEditor() {
const ref = React.useRef(null);
const [editorState, setEditorState] = React.useState(
EditorState.createEmpty()
);
const handleKeyCommand = command => {
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
setEditorState(newState)
return "handled"
}
return "not-handled";
}
const onItalicClick = event => {
ref.current.focus()
EditorState.moveFocusToEnd(editorState)
setEditorState(RichUtils.toggleInlineStyle(editorState, 'ITALIC'))
}
const onUnderLinkClick = event => {
event.preventDefault()
setEditorState(RichUtils.toggleInlineStyle(editorState, "UNDERLINE"))
}
const onBoldClick = event => {
event.preventDefault()
console.log(event)
setEditorState(RichUtils.toggleInlineStyle(editorState, "BOLD"))
}
return <div>
<div>
<Button
onClick={onItalicClick}
>
<Icon type="italic" />
</Button>
<Button
onClick={onUnderLinkClick}
>
<Icon type="underline" />
</Button>
<Button
onClick={onBoldClick}
>
<Icon type="bold" />
</Button>
</div>
<Editor
editorState={editorState}
onChange={editorState => setEditorState(editorState)}
handleKeyCommand={handleKeyCommand}
ref={ref}
/>
</div>;
}
export default MyEditor;
SCSS
.wrapper {
border: 1px solid #e2e2e2;
padding: 10px;
margin-bottom: 20px;
}
selectionState = this.state.editorState.getSelection()
selectionState=selectionState.merge({'forceKey':xxxx, focusOffset:5})
here you can set focusOffset to be the text length of that block.

Material-ui: open menu by event hover

Currently the menuItem only opens your children after a click. Is there an attribute that I agree to open via Hover?
<MenuItem key={index}
menuItems={menuitems}
**onHover={true}**
>
menuItem
</MenuItem>
There is not specific attribute available through the material-ui library. However, you could create this yourself pretty easily using the onMouseOver event.
I've adapted the Simple Menu example from the material-ui site to show you how this can be done:
import React from 'react';
import Button from 'material-ui/Button';
import Menu, { MenuItem } from 'material-ui/Menu';
class SimpleMenu extends React.Component {
state = {
anchorEl: null,
open: false,
};
handleClick = event => {
this.setState({ open: true, anchorEl: event.currentTarget });
};
handleRequestClose = () => {
this.setState({ open: false });
};
render() {
return (
<div>
<Button
aria-owns={this.state.open ? 'simple-menu' : null}
aria-haspopup="true"
onClick={this.handleClick}
{ // The following line makes the menu open whenever the mouse passes over the menu }
onMouseOver={this.handleClick}
>
Open Menu
</Button>
<Menu
id="simple-menu"
anchorEl={this.state.anchorEl}
open={this.state.open}
onRequestClose={this.handleRequestClose}
>
<MenuItem onClick={this.handleRequestClose}>Profile</MenuItem>
<MenuItem onClick={this.handleRequestClose}>My account</MenuItem>
<MenuItem onClick={this.handleRequestClose}>Logout</MenuItem>
</Menu>
</div>
);
}
}
export default SimpleMenu;
I got it to work by upping the z-index of the button. Otherwise, the mouse technically goes out of the button when the modal appears on top of the button. Then the menu will close since the user is no longer hovering.
If you add onMouseLeave to Menu then onMouseLeave will only trigger if you go out of the browser. So instead, I added onMouseLeave to MuiList which doesn't take up the whole page.
I also added need some extra conditionals in the handleOpen to account for if the mouse leaves the button but enters the menu.
import React, { useState } from "react";
import Button from "#material-ui/core/Button";
import Menu from "#material-ui/core/Menu";
import { createMuiTheme, ThemeProvider } from "#material-ui/core/styles";
const theme = createMuiTheme({});
const MyMenu = () => {
const [anchorEl, setAnchorEl] = useState(null);
const [open, setOpen] = useState(false);
const handleOpen = (event) => {
setAnchorEl(event.currentTarget);
setOpen(true);
};
const handleClose = (e) => {
if (e.currentTarget.localName !== "ul") {
const menu = document.getElementById("simple-menu").children[2];
const menuBoundary = {
left: menu.offsetLeft,
top: e.currentTarget.offsetTop + e.currentTarget.offsetHeight,
right: menu.offsetLeft + menu.offsetWidth,
bottom: menu.offsetTop + menu.offsetHeight
};
if (
e.clientX >= menuBoundary.left &&
e.clientX <= menuBoundary.right &&
e.clientY <= menuBoundary.bottom &&
e.clientY >= menuBoundary.top
) {
return;
}
}
setOpen(false);
};
theme.props = {
MuiList: {
onMouseLeave: (e) => {
handleClose(e);
}
}
};
return (
<div>
<ThemeProvider theme={theme}>
<Button
id="menubutton1"
aria-owns={open ? "simple-menu" : null}
aria-haspopup="true"
onMouseOver={handleOpen}
onMouseLeave={handleClose}
style={{ zIndex: 1301 }}
>
Open Menu
</Button>
<Menu
id="simple-menu"
anchorEl={anchorEl}
open={open}
anchorOrigin={{
vertical: "bottom",
horizontal: "center"
}}
transformOrigin={{
vertical: "top",
horizontal: "center"
}}
>
Menu
<br />
Items
</Menu>
</ThemeProvider>
</div>
);
};
export default MyMenu;
CodeSandbox
I have added mouseLeave listener on container div to close the menu, and mouseOver listener on menu button to open menu. This worked for me...
<div onMouseLeave={closeMenu}>
<button onMouseOver=(openMenu) />
<Menu />
<MenuItems />
</div>
This is how I did it:
https://codesandbox.io/s/mui-menu-hover-to-show-dropdown-iguukw?file=/src/TopMenu.tsx
I used on onMouseLeave and onMouseEnter events to control when to show and hide the dropdown menus.
I also used a string state to determine which dropdown menu should show. Only one dropdown menu should show at one moment of time.

Resources