How can I do dynamic badge on React CoreUI? - javascript

I have a problem with a badge on Core UI. I have a Sidebar and one of the elements is Chat. When a new message comes, the badge must show new message. But the old developer have written different ways and I can not change it. I cannot find anything for this.
My codes
Sidebar elements
const _nav = [
{
_tag: "CSidebarNavItem",
name: "Chat",
to: "/chat",
filter: "feedbacks",
icon: "cil-speech",
badge: {
color: "info",
text: "NEW MESSAGE",
},
},
]
My React component
import navigation from "./_nav";
const [filteredNav, setFilteredNav] = useState([]);
const [chatBadge, setChatBadge] = useState(false);
const handleChatBadge = () => {
setChatBadge(true)
}
// it is a sidebar element for user-role
useLayoutEffect(() => {
allwedMenu().then((res) => {
const arr = [navigation[0]];
res.data.items.forEach((element) => {
arr.push(element.name);
});
setFilteredNav(navigation.filter((item) => arr.includes(item.filter)));
});
}, []);
<CSidebarNav>
<CCreateElement
items={filteredNav}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle,
}}
/>
</CSidebarNav>
I need the badge to show when chatBadge is true. But I don't know how can I write this.

You can only add a condition to show the badge when chatBadge is true.
Based on the Value of ChatBadge, you can use the property of the Component CSideBarNavItem to display the badge and pass the colour and text properties.
Here's the updated code:
<CSidebarNav>
<CCreateElement
items={filteredNav}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle,
}}
/>
{filteredNav.map((item, index) => (
<CSidebarNavItem
key={index}
name={item.name}
to={item.to}
icon={item.icon}
badge={
item.name === "Chat" && chatBadge
? { color: "info", text: "NEW MESSAGE" }
: null
}
/>
))}
</CSidebarNav>
Hope it helps.

Related

Unfocusing dropdown menu sets its state as undefined

I'm making a dropdown menu that allows user to set a state and then see the page corresponding to chosen values.
I isolated my code to fully reproduce the issue both in text and in this [CodeSandbox]
Desired baheviour - Open menu, set state using its componets, close menu and keep the state.
Current behaviour - Open menu, set state using its components, close menu and state is set to undefined.
I track the changes to the state in the console and can clearly see that adding items to filter is seen in the updated state every time. However when I close the menu the state changes to undefined and the state is unsuable for my needs.
How do I change the code so the state persists when the menu is closed?
Thanks in advance for your time!
import React from "react";
import { default as ReactSelect } from "react-select";
import { components } from "react-select";
export default function BettingDeck(props) {
const sportsOptions = [
{ value: "soccer", label: "Soccer" },
{ value: "dota", label: "Dota 2" },
{ value: "tennis", label: "Tennis" },
{ value: "csgo", label: "CS:GO" }
];
const Option = (props) => {
return (
<div>
<components.Option {...props}>
<input
type="checkbox"
checked={props.isSelected}
onChange={() => null}
/>{" "}
<label>{props.label}</label>
</components.Option>
</div>
);
};
const [sportsSelectorState, setSportsSelectorState] = React.useState({
optionSelected: [],
isFocused: true
});
function handleChange(selected) {
setSportsSelectorState(() => {
return { optionSelected: selected };
});
}
console.log(sportsSelectorState.optionSelected);
return (
<>
<div className="betting-deck-container">
<div className="betting-deck-head-container">
<div className="betting-deck-title">Betting Deck</div>
{/* <SportsSelector /> */}
<span
class="d-inline-block"
data-toggle="popover"
data-trigger="focus"
data-content="Please selecet account(s)"
onBlur={() => {
setSportsSelectorState({ isFocused: false });
}}
onFocus={() => {
setSportsSelectorState({ isFocused: true });
}}
style={
sportsSelectorState.isFocused ? { zIndex: 1 } : { zIndex: 0 }
}
>
<ReactSelect
options={sportsOptions}
isMulti
closeMenuOnSelect={false}
hideSelectedOptions={false}
components={{
Option
}}
onChange={handleChange}
allowSelectAll={true}
value={sportsSelectorState.optionSelected}
placeholder="Select sports to filter"
menuPortalTarget={document.body}
classNamePrefix="mySelect"
/>
</span>
</div>
</div>
</>);}
Every time you set the state, you overwrite it with a new object.
So this:
setSportsSelectorState(() => {
return { optionSelected: selected };
});
practically removes isFocused from the object.
And this removes optionSelected:
setSportsSelectorState({ isFocused: true });
So to always preserve the entire object, spread the previous state (object) into the new and only overwrite the relevant property:
// The parameter in the callback function (prev)
// always holds the previous state, or should
// I say the state as it currently is
// before you change it.
setSportsSelectorState((prev) => {
return { ...prev, isFocused: true };
});
// or
setSportsSelectorState((prev) => {
return { ...prev, optionSelected: selected };
});

ToolTip does not disappear on scroll

I have a button on the site and a ToolTip to it, which describes the action of the button.
But there is one bug that I can not solve (and I'm already starting to doubt if there is a solution to this problem).
Description of the problem: when the user hovers over the icon, a tooltip appears - everything works fine here. But if at this moment the table is scrolling, then the tooltip flies out of bounds. It's hard to describe, take a look
Pay attention to how the tooltip (if the cursor is hovered over) flies up or down when scrolling.
Tell me how to solve this problem?
<div>
<Tooltip
title="Delete"
arrow
componentsProps={{
tooltip: {
sx: {
bgcolor: '#a3a3a3',
'& .MuiTooltip-arrow': {
color: '#a3a3a3',
},
},
},
}}
PopperProps={{
modifiers: [
{
name: "offset",
options: {
offset: [0, -8],
},
},
],
}}>
<DeleteForeverIcon/>
</Tooltip>
</div>
Instruction: hover over any cell from the first column, wait for the tooltip to appear. Then scroll the wheel up or down and see how the tooltip goes outside the table
P.s. Please note that this question has already been answered. And in principle this solution is working. But I had a lot of problems when adding this solution to my real code. Probably a simple solution for me here would be to simply cancel the scrolling when you hover over the button. Tell me how this can be done (but keep in mind that position: fixed is not suitable in this case)
My approach is different, where each tooltip maintains its own state. It is using IntersectionObserver to determine if the ToolTip component is viewable. When the component is no longer viewable, it will hide the Popper (the tooltip popup) by setting the CSS to display: 'none' via the sx prop on PopperProps.
Codesandbox Example: Here
Here is the modified file FileDownloadButton.jsx:
import React from "react";
import FileDownloadIcon from "#mui/icons-material/FileDownload";
import { ButtonGroup, Tooltip } from "#mui/material";
export default function FileDownloadButton() {
const tipRef = React.useRef(null);
const [inView, setInView] = React.useState(false);
const cb = (entries) => {
const [entry] = entries;
entry.isIntersecting ? setInView(true) : setInView(false);
};
React.useEffect(() => {
const options = {
root: null,
rootMargin: "0px"
};
const ref = tipRef.current;
const observer = new IntersectionObserver(cb, options);
if (ref) observer.observe(ref);
return () => {
if (ref) observer.unobserve(ref);
};
}, [tipRef]);
return (
<ButtonGroup>
<div>
<Tooltip
ref={tipRef}
title="Download record "
arrow
componentsProps={{
tooltip: {
sx: {
bgcolor: "#a3a3a3",
"& .MuiTooltip-arrow": {
color: "#a3a3a3"
}
}
}
}}
PopperProps={{
sx: { display: inView ? "block" : "none" },
modifiers: [
{
name: "offset",
options: {
offset: [0, -8]
}
}
]
}}
>
<FileDownloadIcon />
</Tooltip>
</div>
</ButtonGroup>
);
}
Changes for reference
Change 1
export default function FileDownloadButton() {
const tipRef = React.useRef(null);
const [inView, setInView] = React.useState(false);
const cb = (entries) => {
const [entry] = entries;
entry.isIntersecting ? setInView(true) : setInView(false);
};
React.useEffect(() => {
const options = {
root: null,
rootMargin: "0px"
};
const ref = tipRef.current;
const observer = new IntersectionObserver(cb, options);
if (ref) observer.observe(ref);
return () => {
if (ref) observer.unobserve(ref);
};
}, [tipRef]);
Change 2
PopperProps={{
sx: { display: inView ? "block" : "none" },
Update 1
Original poster wants toggle
Codesandbox example
import React, { useState } from "react";
import FileDownloadIcon from "#mui/icons-material/FileDownload";
import { ButtonGroup, IconButton, Tooltip } from "#mui/material";
import VisibilityOffIcon from "#mui/icons-material/VisibilityOff";
import VisibilityIcon from "#mui/icons-material/Visibility";
export default function FileDownloadButton() {
const [click, setClick] = useState(true);
const tipRef = React.useRef(null);
const [inView, setInView] = React.useState(false);
const cb = (entries) => {
const [entry] = entries;
entry.isIntersecting ? setInView(true) : setInView(false);
};
React.useEffect(() => {
const options = {
root: null,
rootMargin: "0px"
};
const ref = tipRef.current;
const observer = new IntersectionObserver(cb, options);
if (ref) observer.observe(ref);
return () => {
if (ref) observer.unobserve(ref);
};
}, [tipRef]);
return (
<ButtonGroup>
<div>
<Tooltip
ref={tipRef}
title={click ? "Show item" : "Hide Item"}
arrow
componentsProps={{
tooltip: {
sx: {
bgcolor: "#a3a3a3",
"& .MuiTooltip-arrow": {
color: "#a3a3a3"
}
}
}
}}
PopperProps={{
sx: { display: inView ? "block" : "none" },
modifiers: [
{
name: "offset",
options: {
offset: [0, -8]
}
}
]
}}
>
<IconButton onClick={() => setClick(!click)}>
{click ? <VisibilityOffIcon /> : <VisibilityIcon />}
</IconButton>
</Tooltip>
</div>
</ButtonGroup>
);
}
I think this is browser specific issue. When I checked the given url( https://codesandbox.io/s/silly-grass-1lb3qw) in firefox browser it was working fine(but not in the chrome). Later figured that out hover while scrolling on element will work differently in the chrome compare to other browsers since latest versions.
I made following changes to make it work in chrome. Basically whenever we hover any item then the material tooltip is being added to the document. So what I did was I have attached an scroll event and if there is any material tooltip element is present I just simply removed it.
DeviceTable.jsx
export default function DevicesTable() {
const tableRef = useRef();
function removeElementsByClass(className){
const elements = document.getElementsByClassName(className);
while(elements.length > 0){
elements[0].remove();
}
}
useEffect(() => {
if (tableRef.current) {
tableRef.current.addEventListener("scroll", (e) => {
// CLASS NAME OF THE TOOLTIP ATTACHED TO THE DOM. THERE ARE MULTIPLE CLASSES BUT I FOUND FOLLOWING CLASSNAME TO BE UNIQUE. PLEASE CROSS CHECK FROM YOUR END AS WELL.
//YOU CAN CHECK THIS BY PASSING open={true} attribute on <Tooltip> AND INSPECT DOM
removeElementsByClass("css-yk351k-MuiTooltip-tooltip")
});
}
return () => {
if(tableRef.current) {
tableRef.current.removeEventListener("scroll", ()=>{});
}
}
}, []);
return (
<TableContainer className="TableContainerGridStyle">
<Table className="TableStyle">
<DevicesTableHeader />
// CHANGED LINE
<TableBody ref={tableRef} className="TableBodyStyle">
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
<DevicesTableCell />
</TableBody>
</Table>
</TableContainer>
);
}
Apart from the above I think you can use another alternatives like followCursor, setting the position relative attribute to the table cell(TableCellStyle) or body. But these don't solve the problem fully.
As you are passing Table component as props children to the StateLabel component so in order to display/render we need to update StateLabel component to use props.children
export default function StateLabel({children}) {
return <div>{children}</div>;
}
Div hover not working when scrolling in chrome

Using a Prop to populate an array - React useState

I have 3 dropdowns, each which controls state. When this dropdown is selected it will set the target and send it to redux. For example.
const [interviewStep1, setinterviewStep1] = useState('Phone Screening')
const [interviewStep2, setinterviewStep2] = useState('Formal Interview')
const [interviewStep3, setinterviewStep3] = useState('Reference Check')
This is sent to redux in this manner.
<Dropdown_Content>
{interviewStageSelection.map((option) => (
<Dropdown_Item
key={option}
onClick={(e) => {
setinterviewStep1(option)
setisActive(!isActive)
console.log(interviewStep1)
setisActive(false)
updateInterview1(dispatch, option)
}}
>
<Typography variant="subtitle5" color="black" sx={{ "&:hover": { color: "white" } }}>{option}</Typography>
</Dropdown_Item>
))}
</Dropdown_Content>
I then pass this state as props into my next component.
export default function JobPostInterviewVerticalStepper(interviewStep1, interviewStep2, interviewStep3)
this does come through, but then I want to display in my array. How do I use these props?
const steps = [
{
label: 'Phone Screening',
//I WANT A interviewStep1 here!
},
{
label: 'Formal Interview',
},
{
label: 'Reference Check',
},
{
label: 'Offer',
},
];

Getting content of currently active Text component wrapped inside popover of antd

I am using antd components for my react app. I have a Text component wrapped inside of Popover component. Now in my case this Popover is applied to one particular column of table, i.e. every row-element in that column has a Popover component rendered for it upon mouse hovering.
title: "Name",
dataIndex: "name",
key: "name-key",
sortType: "string",
sortDirections: ["descend", "ascend"],
sorter: (a, b) => a.name.length - b.name.length,
render: (text, record) => (
<Popover>
<Text onMouseOver={handleOnMouseOverCommitId}> {name} </Text>
</Popover>
)
I want to get hold of the row-element's value, the one contained by the above Text component whenever I hover over it. In this case the value denoted by {name} above.
I tried getting it with e.target.value via onMouseOver event, but it returned undefined.
I think I get the reason behind it, because the event.target returns an html node of type <span>.
With a normal div element e.target.value has worked in the past for me. But doing the same thing with a predefined component like antd's Text seems a bit trickier.
Just to elaborate, the Popover has two buttons and based on which button user clicks, I need to render some other components, something like an overlay component.
But in order to do that I would also need to get hold of the text value which originally triggered the Popover.
Below is the code(most of the things removed for preciseness).
record.name is what I ultimately need to capture.
<Popover
content={
<>
<Space>
<Button onClick={showSomeOverlayPaneForName}>
{"View Details for record.name"}
</Button>
<Button href={"https://abc.xyz.com/" + record.role}>
{"View Role Details"}
</Button>
</Space>
</>
}
trigger={"hover"}
>
<Text style={{"color": blue.primary}} copyable={true} onMouseOver={handleOnMouseOverName}>{record.name}</Text>
</Popover>
The handleOnMouseOverName function(which doesn't work anyway) :
const handleOnMouseOverName = (e) => {
//console.log("e.target.value :--- ", e.target.value);
setCurrentActiveName(e.target.value)
}
And once my currentActiveName variable is set(via useState), I use that value inside my function showSomeOverlayPaneForName
const showSomeOverlayPaneForName = (e) => {
axios
.get(
`some-url`,
{
params: {name: currentActiveName}
}
)
.then((response) => {
setData(response.data);
}).catch(reason => {
//xyz
});
}
You need to pass on the record of the enclosing render function to the handleOnMouseOverName function.
Check the following example
import React from 'react';
import 'antd/dist/antd.css';
import './index.css';
import { Space, Table, Button, Popover } from 'antd';
const App = () => {
const data = [
{
key: '1',
name: 'John Brown',
address: 'New York No. 1 Lake Park',
role: 'admin',
},
{
key: '2',
name: 'Jim Green',
address: 'London No. 1 Lake Park',
role: 'user',
},
{
key: '3',
name: 'Joe Black',
address: 'Sidney No. 1 Lake Park',
role: 'manager',
},
];
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (name, record) => {
const content = (
<>
<Space>
<Button
onClick={() => {
viewDetail(record);
}}
>
{'View Details for ' + record.name}
</Button>
<Button href={'https://abc.xyz.com/' + record.role}>
{'View Role Details'}
</Button>
</Space>
</>
);
return (
<>
<Popover content={content} title="Details">
<div
onMouseOver={() => {
handleOnMouseOverName(record);
}}
>
{name}
</div>
</Popover>
</>
);
},
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
const handleOnMouseOverName = (record) => {
console.log(record);
};
const viewDetail = (record) => {
console.log(record);
};
return <Table columns={columns} dataSource={data} />;
};
export default App;
Output:
I hope this helps.
From antd docs: https://ant.design/components/popover/#header
Apparently you're supposed to render the <Popover/> with a content={content}-prop
For example
const content = <div>Content to render under title</div>
const App = () => {
const value = "Text to hover";
return (
<Popover content={content} title="Title">
<Text>{value}</Text>
</Popover>
)
}

React ES6: Get selected value in dropdown list using semantic UI

Given the following data, how can I get the birds name and push it (Using the add button) to a new array to be displayed in another div (Using react es6)? So basically I want a user to click a bird from the semantic dropdown and display it in a different div for example shown below. This is probably simple but I can't seem to find a way to it when I'm using Semantic elements. Do I need to use onChange?
I need to to do this in a class I am exporting (react) (just havent shown the class/constructor/state definitions)
<div>
<p>How can i display 'Bird_Name' here?<p>
</div>
addClick = () => {
}
const {
Button,
Container,
Divider,
Dropdown,
Header,
Message,
Segment,
} = semanticUIReact
const birds = [
{
"value": "001",
"Bird_Name": "Eurasian Collared-Dove"
},
{
"value": "002",
"Bird_Name": "Bald Eagle"
},
{
"value": "003",
"Bird_Name": "Cooper's Hawk"
},
];
const options = birds.map(({ ID, Bird_Name }) => ({ value: ID, text: Bird_Name }))
const App = () => (
<Container>
<Divider hidden />
<Header as='h1'>Semantic-UI-React</Header>
<Dropdown
placeholder='Select...'
selection
search
options={options}
renderLabel={({ Bird_Name }) => 1}
/>
<button className="ui primary button add" onClick={this.addClick}>Add</button>
</Container>
)
// ----------------------------------------
// Render to DOM
// ----------------------------------------
const mountNode = document.createElement('div')
document.body.appendChild(mountNode)
ReactDOM.render(<App />, mountNode)
So, what you basically want is the onChange function which will display.
<Dropdown
placeholder='Select...'
selection
search
options={options}
renderLabel={({ Bird_Name }) => 1}
onChange={this.getBird}
/>
and make a getBird function
getBird = (event, {value}) => {
console.log(value);
let bird_name = event.target.textContent;
console.log(bird_name);
}
The value and text variable in the getBird function are basically the value and bird_name of the selected bird from the dropdown.

Categories

Resources