Problem:
I have created a bar chart with a custom tooltip. Now What I need is to position tooltip on top of the bar not within the area of the chart. Like in this picture.
This is how It looks like now.
Here I am providing how I organize my code.
import React, { Component } from "react";
import {
BarChart,
Tooltip,
Bar,
Legend,
ResponsiveContainer,
Cell
} from "recharts";
import { Card, CardTitle, CardBody } from "reactstrap";
import "./SessionDuration.css";
const colors = ["#26a0a7", "#79d69f", "#f9ec86", "#ec983d"];
const data = [
{
name: "Page A",
pv: 2400,
amt: 2400
},
{
name: "Page B",
pv: 1398,
amt: 2210
},
{
name: "Page C",
pv: 9800,
amt: 2290
},
{
name: "Page D",
pv: 3908,
amt: 2000
}
];
const getTitleOfTooltip = (label) =>{
if (label === 0) {
return "<=5 min";
}
if (label === 1) {
return "5-30 min";
}
if (label === 2) {
return "30-60 min";
}
if (label === 3) {
return ">60";
}
}
const getIntroOfPage = label => {
if (label === 0) {
return "Page A is about men's clothing";
}
if (label === 1) {
return "Page B is about women's dress";
}
if (label === 2) {
return "Page C is about women's bag";
}
if (label === 3) {
return "Page D is about household goods";
}
};
class SessionDuration extends Component {
render() {
return (
<Card className="session-duration-card">
<CardTitle className="session-duration-card-header">
Session Duration
</CardTitle>
<CardBody>
<ResponsiveContainer width="100%" height="100%" aspect={4.0 / 5.5}>
<BarChart
data={data}
margin={{
top: 3,
right: 5,
left: 5,
bottom: 13
}}
barGap={10}
>
<Tooltip
coordinate={{ x: 0, y: 150 }}
content={<CustomTooltip />}
/>
<Bar dataKey="pv" fill="#8884d8">
{data.map((entry, index) => (
<Cell key={`cell-${index + 1}`} fill={colors[index]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</CardBody>
</Card>
);
}
}
export default SessionDuration;
const CustomTooltip = ({ active, payload, label }) => {
if (active) {
return (
<div className="session-duration-tooltip">
<p className="session-duration-tooltip-label">{getTitleOfTooltip(label)}</p>
<p className="session-duration-tooltip-intro">
{getIntroOfPage(label)}
</p>
<p className="session-duration-tooltip-desc">
Anything you want can be displayed here.
</p>
</div>
);
}
return null;
};
Here I am providing my CSS file.
#media only screen and (min-width: 1024px) {
.session-duration-card {
margin-top: 14.5%;
margin-left: -44%;
width: 190% !important;
border-radius: none !important;
height: 86%
}
.session-duration-card-header {
font-weight: 700;
text-align: left;
padding-left: 6%
}
.session-duration-tooltip {
width: 210%;
}
.session-duration-tooltip-label {
font-weight: 700;
font-size: 11px;
}
}
#media only screen and (min-width: 308px) and (max-width: 1024px) {
.session-duration-card {
margin-top: 5%;
margin-left: 3.2%;
width: 94.5%;
border-radius: none !important;
}
.session-duration-card-header {
font-weight: 700;
text-align: center;
}
}
#media only screen and (min-width: 1666px) {
.session-duration-card {
margin-top: 11%;
margin-left: -13%;
width: 190% !important;
height: 97%;
border-radius: none !important;
}
.session-duration-card-header {
font-weight: 700;
text-align: center;
}
}
I tried a lot to find a solution for my problem but I was unable to get it done can someone help me by modifying my code to get this work done. Thank you very much.
To solve this problem, will need to get the height and width of each bar and tooltip.
Need two functions onMouseMove and onMouseOut to get the height
and width of the Bar component.
Using useState to get and keep the height and width to the state, also place extra boolean to the state, to handle when the mouse is outside of the Bar. (So, to function we add object with two paramets data and boolean ).
In the Tooltip component, set the position object for static values X, Y and using the state values, set the height and width.
Using useEffect, to find the .recharts-tooltip-wrapper class, to define the height and width of the DOM element.
Set styles for .recharts-tooltip-wrapper. (Rechart. js in .recharts-tooltip-wrapper has its own generated style, we need to keep some static style of the .recharts-tooltip-wrapper and add our).
SessionDuration.js
import React, { useState, useEffect } from 'react';
import { BarChart, Tooltip, Bar, ResponsiveContainer, Cell } from 'recharts';
import { Card, CardTitle, CardBody } from 'reactstrap';
import './SessionDuration.css';
const colors = ['#26a0a7', '#79d69f', '#f9ec86', '#ec983d'];
const data = [
{
name: 'Page A',
pv: 2400,
amt: 2400,
},
{
name: 'Page B',
pv: 1398,
amt: 2210,
},
{
name: 'Page C',
pv: 9800,
amt: 2290,
},
{
name: 'Page D',
pv: 3908,
amt: 2000,
},
];
const getTitleOfTooltip = label => {
if (label === 0) {
return '<=5 min';
}
if (label === 1) {
return '5-30 min';
}
if (label === 2) {
return '30-60 min';
}
if (label === 3) {
return '>60';
}
};
const getIntroOfPage = label => {
if (label === 0) {
return "Page A is about men's clothing";
}
if (label === 1) {
return "Page B is about women's dress";
}
if (label === 2) {
return "Page C is about women's bag";
}
if (label === 3) {
return 'Page D is about household goods';
}
};
export const SessionDuration = () => {
const [position, setPosition] = useState(null);
useEffect(() => {
const tooltip = document.querySelector('.recharts-tooltip-wrapper');
if (!tooltip) return;
// Init tooltip values
const tooltipHeight = tooltip.getBoundingClientRect().height;
const tooltipWidth = tooltip.getBoundingClientRect().width;
const spaceForLittleTriangle = 10;
// Rewrite tooltip styles
tooltip.style = `
transform: translate(${position?.data.x}px, ${position?.data.y}px);
pointer-events: none; position: absolute;
top: -${tooltipHeight + spaceForLittleTriangle}px;
left: -${tooltipWidth / 2 - position?.data.width / 2}px;
opacity: ${position?.show ? '1' : 0};
transition: all 400ms ease 0s;
`;
}, [position]);
return (
<>
<Card className="session-duration-card">
<CardTitle className="session-duration-card-header">
Session Duration
</CardTitle>
<CardBody>
<ResponsiveContainer width="100%" height="100%" aspect={4.0 / 5.5}>
<BarChart
data={data}
margin={{
top: 100, // for tooltip visibility
right: 5,
left: 5,
bottom: 13,
}}
barGap={10}
>
<Tooltip
cursor={false} // hide hover effect 'grey rectangle'
position={{
// Static position
x: position?.data.x ?? 0,
y: position?.data.y ?? 0,
}}
content={<CustomTooltip />}
/>
<Bar
dataKey="pv"
fill="#8884d8"
// Handlers
onMouseMove={data => setPosition({ data: data, show: true })}
onMouseOut={data => setPosition({ data: data, show: false })}
>
{data.map((entry, index) => (
<Cell key={`cell-${index + 1}`} fill={colors[index]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</CardBody>
</Card>
</>
);
};
export default SessionDuration;
const CustomTooltip = ({ active, payload, label }) => {
if (active) {
return (
<div className="session-duration-tooltip">
<p className="session-duration-tooltip-label">
{getTitleOfTooltip(label)}
</p>
<p className="session-duration-tooltip-intro">
{getIntroOfPage(label)}
</p>
<p className="session-duration-tooltip-desc">
Anything you want can be displayed here.
</p>
</div>
);
}
return null;
};
SessionDuration.css
:root {
--bg-color: hsl(270 3% 29% / 0.9);
}
.session-duration-tooltip {
max-width: 250px;
position: relative;
padding: 0.5em;
border-radius: 5px;
background-color: var(--bg-color);
color: aliceblue;
}
.session-duration-tooltip::after {
content: '';
height: 0;
width: 0;
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
border-style: solid;
border-width: 10px 10px 0 10px;
border-color: var(--bg-color) transparent transparent transparent;
}
A lot of people asking for this feature here https://github.com/recharts/recharts/issues/222 and here https://github.com/recharts/recharts/issues/488. I came up with
Third working solution (with great help from #ЖнецЪ answer)
Unfortunately the problem with that answer is that you still have to hover the "bars" themselves, and mine are quite narrow, so I wanted it to show up regardless of if you are hovering the bar or the "cursor area", this solution allows the latter.
Basically:
Get the active tooltip index from the full barchart (changes on the cursor area and not the specific bar)
a useEffect hook grabs all the bars using the bar class ".recharts-bar-rectangle" and grabs the current active one using the toolTipIndex, get's the height, and sets that to the tooltip Y-position.
As before the tooltip is controlled by the internal x position but the y position is the height +/- some offset.
export default function BarChartCard(props) {
const [activeBarIndex, setActiveBarIndex] = useState();
const [toolTipYPosition, setToolTipYPosition] = useState({});
useEffect(() => {
const barCharts = document.querySelectorAll(".recharts-bar-rectangle");
if (!barCharts || !activeBarIndex) return;
// Init tooltip values
const barChart = barCharts[activeBarIndex];
setToolTipYPosition(barChart.getBoundingClientRect().height);
}, [activeBarIndex]);
return (
<BaseCard>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h2">{props.title}</Typography>
</Grid>
</Grid>
<ResponsiveContainer width={"100%"} height={300}>
<BarChart
barGap={0}
data={props.data}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
onMouseMove={(e) => {
setActiveBarIndex(e.activeTooltipIndex);
}}
>
<Tooltip
content={<CustomTooltip />}
cursor={false}
position={{ y: 170 - toolTipYPosition }}
offset={-60}
allowEscapeViewBox={{ x: true, y: true }}
/>
<XAxis
style={{
fontSize: "1.125rem",
fontFamily: "Cairo",
fontWeight: 600,
}}
axisLine={false}
tickLine={false}
dataKey="date"
tickFormatter={ReformatDateShortDayName}
/>
<YAxis
allowDecimals={false}
style={{
fontSize: "1.125rem",
fontFamily: "Cairo",
fontWeight: 600,
}}
dataKey="value"
axisLine={false}
tickLine={false}
/>
<Legend
style={{
fontSize: "1.125rem",
fontFamily: "Cairo",
fontWeight: 600,
}}
iconType="circle"
iconSize={6}
align="right"
verticalAlign="top"
height={36}
formatter={(value) => (
<span
style={{
color: "#CCC",
fontSize: "1.125rem",
fontWeight: 600,
fontFamily: "Cairo",
}}
>
{value}
</span>
)}
/>
<Bar
barSize={10}
dataKey="value"
name={props.legendText}
fill="#EFC92B"
/>
</BarChart>
</ResponsiveContainer>
</BaseCard>
);
}
A hacky solution
based on some other implementation and answers out there:
const [barGraphData, setBarGraphData] = useState({})
return (
<BarChart barGap={0} data={props.data} margin={{
top: 5,
right: 30,
left: 20,
bottom: 5
}}>
<Tooltip content={<CustomTooltip />}
cursor={{ fill: 'transparent' }}
position={{ y: barGraphData.height + 40}}
offset={-60}
/>
<XAxis />
<YAxis />
<Legend />
<Bar
barSize={10}
dataKey="value"
fill="#EFC92B"
onMouseOver={(data)=> {
setBarGraphData(data)
}}
onMouseLeave={(data) => {
setBarGraphData(data)
}}
/>
</BarChart>
)
So basically:
Have a static y-value, in this case I'm doing the height of the bar + some offset (at least I think it's the height of the bar). But this one could be static as well.
Add an offset to the tooltip, I had to do this otherwise it would not be on top of the bar but right shifted 60 pixels.
Let the x coordinate be handled by recharts.
As you can see from my code, I used a custom tooltip according to the recharts documentation
const CustomTooltip = ({ active, payload, label }) => {
const classes = useStyles();
return (
<Fragment>
{active && payload?.length >= 1 &&
<Card className={classes.toolTip}>
<CardContent className={classes.toolTipContent}>
<Typography className={classes.toolTipLabel}>{label}</Typography>
<Typography className={classes.toolTipValue}>{payload[0].payload.value}</Typography>
</CardContent>
</Card>
}
</Fragment>
)
}
But this is just for formatting / style, it should work with the default one as well.
Unfortunately this only seems to work when you hover the actual "Bar" and not the full area as defined by the "cursor".
Second hacky solution, which works for the full cursor area
This led me to a second solution which unfortunately gives an warning
Warning: Cannot update a component (`BarChartCard`) while rendering a different component (`label`). To locate the bad setState() call inside `label`, follow the stack trace as described in ...
The code to create get it going is here, any comment on how to make this work would be awesome!
export default function BarChartCard(props) {
const [barGraphData, setBarGraphData] = useState({});
const [activeBarIndex, setActiveBarIndex] = useState({});
const [toolTipYPosition, setToolTipYPosition] = useState({});
return (
<BaseCard>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h2">{props.title}</Typography>
</Grid>
</Grid>
<ResponsiveContainer
width={"100%"}
height={300}
>
<BarChart
barGap={0}
data={props.data}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
onMouseMove={(e) => {
setActiveBarIndex(e.activeTooltipIndex)
}}
>
<Tooltip
content={<CustomTooltip />}
cursor={false}
position={{ y: -toolTipYPosition + 170}}
offset={-60}
allowEscapeViewBox={{ x: true, y: true }}
/>
<XAxis
style={{
fontSize: "1.125rem",
fontFamily: "Cairo",
fontWeight: 600,
}}
axisLine={false}
tickLine={false}
dataKey="date"
tickFormatter={ReformatDateShortDayName}
/>
<YAxis
allowDecimals={false}
style={{
fontSize: "1.125rem",
fontFamily: "Cairo",
fontWeight: 600,
}}
dataKey="value"
axisLine={false}
tickLine={false}
/>
<Legend
style={{
fontSize: "1.125rem",
fontFamily: "Cairo",
fontWeight: 600,
}}
iconType="circle"
iconSize={6}
align="right"
verticalAlign="top"
height={36}
formatter={(value) => (
<span
style={{
color: "#CCC",
fontSize: "1.125rem",
fontWeight: 600,
fontFamily: "Cairo",
}}
>
{value}
</span>
)}
/>
<Bar
label={(e) => {
if(e.index === activeBarIndex){
setToolTipYPosition(e.height)
}
return null;
}}
barSize={10}
dataKey="value"
name={props.legendText}
fill="#EFC92B"
onMouseOver={(data) => {
setBarGraphData(data);
}}
onMouseLeave={(data) => {
setBarGraphData(data);
}}
/>
</BarChart>
</ResponsiveContainer>
</BaseCard>
);
}
Related
I am creating custom tabs using react and material UI. In these tabs, we don't have a fixed tab count, based on the data, the length of the tab might increase and decrease. So we planned to add scrollable functionality If the tabs count is not occupied in the given space.
But by default, the scroll is appearing even if we have only one data.
below is the code for it.
import { Key, useState } from "react";
import { styled } from "#mui/material/styles";
import Button from "#mui/material/Button";
import { ReactComponent as Plus } from "./plus.svg";
import React from "react";
const Tabs = styled("div")`
width: 100%;
overflow: hidden;
margin: 1em 0 2em;
`;
const TabContainer = styled("ul")(() => ({
padding: 0,
margin: 0,
display: "flex",
flex: "1 1 auto",
overflowX: "auto",
overflowY: "hidden",
"& li": {
"&:first-of-type": {
marginLeft: 0,
"#media (max-width: 991px)": {
marginLeft: 10
}
}
},
"#media (max-width: 400px)": {
display: "unset"
}
}));
const Nav = styled("nav")(() => ({
display: "flex",
"#media (max-width: 991px)": {
textAlign: "center"
}
}));
const Tab = styled("li")(({ theme }) => ({
border: `2px solid ${theme.palette.grey[900]}`,
borderBottom: "none",
margin: "0 10px",
display: "block",
float: "left",
position: "relative",
borderTopRightRadius: 5,
borderTopLeftRadius: 5,
backgroundColor: theme.palette.common.white,
"#media (max-width: 991px)": {
float: "unset",
textAlign: "center"
},
"&.tab-current": {
border: `2px solid ${theme.palette.primary.main}`,
borderBottom: "none",
zIndex: 100,
"&::before": {
content: '""',
position: "absolute",
height: "2px",
right: "100%",
bottom: 0,
width: "1000px",
background: theme.palette.primary.main
},
"&::after": {
content: '""',
position: "absolute",
height: "2px",
right: "100%",
left: "100%",
bottom: 0,
width: "4000px",
background: theme.palette.primary.main
},
"& span": {
color: theme.palette.primary.main
}
}
}));
const Span = styled("span")(({ theme }) => ({
color: theme.palette.grey[900],
display: "block",
fontSize: "24px",
lineHeight: 2.5,
padding: "0 14px",
cursor: "pointer",
fontWeight: 400,
overflow: "hidden",
maxWidth: "ch",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}));
const AddGoalCTA = styled("span")(({ theme }) => ({
color: theme.palette.grey[900],
display: "block",
fontSize: "24px",
lineHeight: 2.5,
padding: "0 24px",
cursor: "pointer",
fontWeight: 900,
overflow: "hidden",
whiteSpace: "nowrap"
}));
const ButtonContainer = styled("div")(() => ({
float: "right",
"#media (max-width: 991px)": {
display: "none"
},
"& .MuiButton-root": {
padding: "10px"
}
}));
const PlusIcon = styled("span")(() => ({
width: "24px",
color: "black"
}));
const tabsData = ["Save For College", "Retirement Saving", "Save For Bike"];
// const tabsData = ["Save For College", "Retirement Saving", "Save For Bike", "Legacy Saving", "Save For Poker", "Save For Money"]
const TabsComponent = ({ hideEditButton, showAddTab = true }: any) => {
const [toggleState, setToggleState] = useState(0);
const toggleTab = (index: any) => {
setToggleState(index);
};
return (
<>
<Tabs>
<Nav>
<TabContainer>
{tabsData?.map((value: string, index: Key | null | undefined) => (
<Tab
className={toggleState === index ? "tab-current" : ""}
onClick={() => toggleTab(index)}
key={index}
tabIndex={0}
role="tab"
>
<Span>{value}</Span>
</Tab>
))}
{showAddTab && (
<Tab
onClick={() => {}}
tabIndex={0}
role="tab"
onKeyPress={() => {}}
>
<AddGoalCTA>
<PlusIcon as={Plus} />
</AddGoalCTA>
</Tab>
)}
</TabContainer>
{!hideEditButton && (
<ButtonContainer>
<Button variant="contained" onClick={() => {}}>
Edit
</Button>
</ButtonContainer>
)}
</Nav>
</Tabs>
</>
);
};
export default TabsComponent;
Here you can find the working demo - https://codesandbox.io/s/mui-tabs-9sgt89?file=/tab.tsx:0-4092
Please help me to resolve this one.
I checked your code. Actually for the current tab &::after has fixed width width: "4000px", which is causing the issue. you can reduce it to 1000px or to your convenience.
Hope this helps!!
Thanks
When I vertically scroll the moving div, some of the content freezes. I'm wondering how I would fix this. It isn't consistent either, as clicking the start/stop button sometimes fixes the issue for a few seconds. Very confused.
import { useEffect, useRef, useState } from "https://cdn.skypack.dev/react"
import ReactDOM from "https://cdn.skypack.dev/react-dom"
const Graph = ({ references: { trackerRef, wholeRef } }) => {
return (
<div
ref={wholeRef}
style={{
overflow: 'scroll',
height: '400px',
backgroundColor: '#333',
cursor: 'default',
userSelect: 'none'
}}
>
<div style={{ position: 'relative' }}>
<div>
{(() => {
const items = []
for (let i = 1; i < 100; i++) {
items.push(
<div
key={i}
style={{
position: 'absolute',
left: i * 1000,
height: '100%',
display: 'flex',
}}
>
<div
style={{
width: '1px',
backgroundColor: '#888',
}}
></div>
<div style={{ color: '#ddd', marginLeft: 8, fontSize: 14 }}>
{i}s
</div>
</div>
)
}
return items
})()}
{(() => {
const items = []
for (let i = 1; i < 1000; i++) {
if ((i * 100) % 1000 === 0) continue
items.push(
<div
key={i}
style={{
position: 'absolute',
left: i * 100,
height: '100%',
display: 'flex',
}}
>
<div
style={{
width: '1px',
backgroundColor: '#555',
}}
></div>
<div style={{ color: '#aaa', marginLeft: 5, fontSize: 10 }}>
{i * 100}ms
</div>
</div>
)
}
return items
})()}
<div
ref={trackerRef}
style={{
position: 'absolute',
height: '100%',
display: 'flex',
width: '1px',
backgroundColor: 'lightgreen',
}}
></div>
</div>
<div>
<div style={{ height: '2000px', width: '20px' }}></div>
</div>
</div>
</div>
)
}
const App = () => {
const trackerRef = useRef(null)
const wholeRef = useRef(null)
const [paused, setPaused] = useState(false)
const animationFrames = useRef([]).current
const intervals = useRef([]).current
const time = useRef(0)
useEffect(() => {
// Increase time when unpaused
intervals.push(setInterval(() => !paused && (time.current += 1), 1))
}, [paused])
useEffect(() => {
const refreshTrackbar = () => {
if (trackerRef.current && wholeRef.current && !paused) {
trackerRef.current.style.left = time.current + 'px'
wholeRef.current.scrollLeft = time.current - 100
requestAnimationFrame(refreshTrackbar)
}
}
animationFrames.push(requestAnimationFrame(refreshTrackbar))
}, [paused])
return (
<>
<h1>Scrollbug</h1>
<button
onClick={() => {
if (paused) {
setPaused(false)
} else {
setPaused(true)
animationFrames.forEach(frame => cancelAnimationFrame(frame))
intervals.forEach(interval => clearInterval(interval))
}
}}
>
{paused ? 'Start' : 'Stop'}
</button>
<br />
<br />
<Graph references={{ trackerRef, wholeRef }} />
</>
)
}
ReactDOM.render(<App />, document.getElementById("root"))
Here is a codepen to test the issue yourself
https://codepen.io/springer268/pen/PomjVvw
EDIT: So I tried this codepen on my mac, and the bug does not happen, which leads me to believe this is a Chrome version/platform specific issue, and not a me issue.
I'm not really sure how to make it stop freezing, but if you added an overflow:scroll; to your #root with a bigger height than your side-way scrolling div, you could scroll over it without freezing.
Making the side-way scrolling div a specific height and overflow-y: hidden; could help as well.
Kind of a workaround, but it works.
this is my code:
const StyledTextInput = styled.TextInput`
background-color: red;
font-family: SF Pro Text Regular;
font-style: normal;
font-weight: normal;
font-size: 18px;
color: #000000;
align-items: flex-end;
justify-content: flex-end;
${(props) =>
props.error &&
`
border-bottom-color: red;
border-bottom-width: 1px;
`};
`;
basically what i am trying to do is increase height of text input but when it have text inside the text is in center, how do i make the text go on bottom?
Give a container to the TextInput and make that container act like the TextInput.
Also, use layout animation for a better experience.
Sample Code
import React, { Component } from 'react';
import {
View, Text, StyleSheet, TextInput, TouchableOpacity, LayoutAnimation, Platform, UIManager
} from 'react-native';
if (Platform.OS === 'android') {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
export default class Calls extends Component {
state={ bigger: false }
focus=() => {
this.textInput.focus();
}
toggle=() => {
const { bigger } = this.state;
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ bigger: !bigger });
}
render() {
const { bigger } = this.state;
return (
<View style={styles.container}>
<TouchableOpacity
onPress={this.focus}
style={[styles.inputContainer, { height: bigger ? 200 : 60 }]}
activeOpacity={1}
>
<TextInput
ref={(e) => { this.textInput = e; }}
defaultValue="ASWIN"
style={styles.input}
/>
</TouchableOpacity>
<TouchableOpacity onPress={this.toggle}>
<Text style={styles.toggle}>Toggle</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
inputContainer: {
width: '90%', justifyContent: 'flex-end', borderWidth: 1,
},
input: {
fontSize: 20, height: 50, width: '100%', padding: 0, paddingHorizontal: 10,
},
toggle: {
fontSize: 20,
marginTop: 10,
backgroundColor: '#00000099',
paddingHorizontal: 10,
paddingVertical: 5,
color: '#fff',
},
});
This is the page component itself, it was working fine before but somewhy it started acting like this.
I didn't update any packages it just broke i think the problem is the parent component height is not defined so the child component sets the height to fit content only.
import React, { Component } from 'react';
import Navbar from './navbar';
import { withStyles } from '#material-ui/core/styles';
import Avatar from '#material-ui/core/Avatar';
import Grid from '#material-ui/core/Grid';
import Chip from '#material-ui/core/Chip';
import amumu from '../Images/amumusad.png';
import Modal from 'react-responsive-modal';
import fortniteDab from '../Images/fortnitedab.png';
import compose from 'recompose/compose';
import { withNamespaces } from 'react-i18next';
import {connect} from 'react-redux';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import {CopyToClipboard} from 'react-copy-to-clipboard';
import Snackbar from '#material-ui/core/Snackbar';
import Tooltip from '#material-ui/core/Tooltip';
import {Helmet} from "react-helmet";
const styles = theme => ({
chip: {
minWidth: 350,
margin: theme.spacing.unit,
fontSize: 14,
[theme.breakpoints.up('sm')]: {
fontSize: 20,
}
},
fab: {
margin: theme.spacing.unit,
fontSize: 10,
minWidth: 250,
maxWidth: 250,
[theme.breakpoints.up('lg')]: {
fontSize: 12,
minWidth: 250,
maxWidth: 250,
}
},
fbAvatar: {
margin: 10,
width: 50,
height: 50,
fontSize: 20,
color: '#fff',
fontWeight: 'bold',
backgroundColor: '#3F51B5',
},
emailAvatar: {
margin: 10,
width: 50,
height: 50,
fontSize: 20,
color: '#fff',
fontWeight: 'bold',
backgroundColor: '#f50057',
},
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
backgroundColor: '#fff',
width: 240
},
resize:{
fontSize:17
},
extendedIcon: {
marginRight: theme.spacing.unit * 3,
},
descStyle: {
fontSize: 12,
color: 'white',
textAlign: 'center',
[theme.breakpoints.up('sm')]: {
fontSize: 15,
}
},
});
const ErrorStyle = {
overlay: {
background: "transparent"
},
modal: {
backgroundColor: 'rgba(219, 105, 105, 0.9)',
color: "white",
borderRadius: '10px',
},
}
const SuccessStyle = {
overlay: {
background: "transparent"
},
modal: {
backgroundColor: 'rgba(124, 214, 105, 0.9)',
color: "white",
borderRadius: '10px',
width: 400
},
}
class Contact extends Component {
state = {
Url: this.props.server.main,
name: "",
email: "",
subject: "",
body: "",
payload: "",
ErrorModal: false,
ErrorMsg: '',
SuccessModal: false,
SuccessMsg: '',
copied: false
}
updateInput(key, value) {
this.setState({ [key]: value });
}
onChange = (value) => {
this.setState({captcha: value})
}
onOpenModal = (type) => {
this.setState({[type]: true });
};
onCloseModal = (type) => {
this.setState({[type]: false });
};
render() {
const { classes } = this.props;
const { t } = this.props;
return (
<div className="GG-BG-INVERSE">
<Helmet>
<title>{t('contactTitle')}</title>
<meta name="description" content={t('contactTitle')} />
</Helmet>
<Navbar page={2}/>
<div className="container">
<div className="BlackBG">
<div style={{height: 70, margin: 10}}>
<Grid container justify="center" alignItems="center">
<Chip
icon={<Avatar className={classes.fbAvatar}>F</Avatar>}
label={t('contactFBTitle')}
className={classes.chip}
color="default"
/>
</Grid>
</div>
<div style={{height: 50}}>
<Grid container justify="center" alignItems="center">
</Grid>
</div>
<Grid container justify="center" alignItems="center">
<Chip
icon={<Avatar className={classes.emailAvatar}>#</Avatar>}
label={t('contactEmailTitle')}
className={classes.chip}
color="default"
/>
</Grid>
<ListItem className={classes.descStyle}>
<ListItemText disableTypography primary={t('contactRule1')} />
</ListItem>
<ListItem className={classes.descStyle}>
<ListItemText disableTypography primary={t('contactRule2')} />
</ListItem>
<Snackbar
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
open={this.state.copied}
onClose={()=>{this.setState({ copied: false })}}
transitionDuration={500}
autoHideDuration={1000}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={<h4 id="message-id">{t('copiedEmail')}</h4>}
/>
</div>
</div>
<Modal open={this.state.ErrorModal} onClose={this.onCloseModal.bind(this,'ErrorModal')} center
styles={ErrorStyle}>
<h3 className="col-xs-6">{this.state.ErrorMsg}</h3>
<img style ={{width: 150, height: 120}} className="col-xs-6" src={amumu} alt=""></img>
</Modal>
<Modal open={this.state.SuccessModal} onClose={this.onCloseModal.bind(this,'SuccessModal')} center
styles={SuccessStyle}>
<h3 className="col-xs-6">{this.state.SuccessMsg}</h3>
<img style ={{width: 150, height: 120}} className="col-xs-6" src={fortniteDab} alt=""></img>
</Modal>
</div>
);
}
}
function mapStateToProps(state){
return {
server: state.server
}
}
export default compose(
withStyles(styles),
withNamespaces(),
connect(mapStateToProps),
)(Contact);
The background image only covers the content and then breaks after leaving the left space on the screen blank, as shown in this picture:
Here's my css code for the background image:
.blackBackground {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-color: #121212;
}
I would say the div containing the background image is the issue here. Make sure that the div expands the full width and height so then the background image will follow.
Try adding width: 100%; to
.blackBackground {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-color: #121212;
width: 100%
}
After your question edit:
Your container is wrapping the black background. You can try container-fluid or rework your code like this:
<Navbar page={2}/>
<div className="BlackBG">
<div className="container">
...
This way the black background is not being constrained by the width of the container.
The code below has three posts photos in the array. When I click on each post button am supposed to be seeing
Three post Photo div that corresponds to each post all at the bottom.
My Problem:
My issue is that it is showing just one post photo div which keeps replacing others after I added the CSS code below.
const mainArea={
position: 'fixed',
width: '80%',
bottom: '0%',
display: 'inline-block'
}
const photodiv={
position: 'relative',
width: '250px',
// height:auto,
background: 'orange',
color: 'black',
borderRadius: '5px 5px 0px 0px',
bottom: '0px',
}
screenshot showing jammed div based on the CSS implementation
What I want:
I need to be seeing three div post photos if the three toggle button is clicked
Here is the main code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
const mainArea={
position: 'fixed',
width: '80%',
bottom: '0%',
display: 'inline-block'
}
const photodiv={
position: 'relative',
width: '250px',
// height:auto,
background: 'orange',
color: 'black',
borderRadius: '5px 5px 0px 0px',
bottom: '0px',
}
class Focus extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
shown: true,
};
}
componentDidMount() {
this.setState({
data: [
{ id: "1", title: "my first title", image: "http://localhost/apidb_react/1.png", visible: true , photoVisible: true},
{ id: "2", title: "my second title", image: "http://localhost/apidb_react/2.png", visible: true, photoVisible: true},
{ id: "3", title: "my third title", image: "http://localhost/apidb_react/3.png", visible: true, photoVisible: true}
]
});
}
toggle(id) {
const newData = this.state.data.map(item => {
if(item.id === id) {
return { ...item, visible: !item.visible};
}
return item;
})
this.setState({
data: newData
});
}
/*
hideUnhidePhoto(id) {
const newData = this.state.data.map(item => {
alert(id);
if(item.id === id) {
alert('ttto ' +item.id);
return { ...item, photoVisible: !item.photoVisible};
}
return item;
})
this.setState({
data: newData
});
}
*/
hideUnhidePhoto(id) {
this.setState(({ data }) => {
return {
data : data.map(item => ({
...item,
photoVisible : (id == item.id) ? !item.photoVisible : item.photoVisible }))
}
});
}
render() {
return (
<div>
<label>
<ul>
{this.state.data.map((post, i) => (
<li key={i}>
<div style={mainArea}>
<div style={photodiv}>
<div style={{ display: post.visible ? "none" : "block"}}>
<b>Post Data:</b> {post.title} --{post.id} <br />
<span style={{color: 'red'}} onClick={ () => this.hideUnhidePhoto(post.id) }> Hide/Unhide Photo</span>
<div style={{ display: post.photoVisible ? "block" : "none"}}>
<img src={post.image} />
</div>
</div></div>
</div>
<button onMouseDown={ () => this.toggle(post.id) }>Toggle </button><br />
<br />
</li>
))}
</ul>
</label>
</div>
);
}
}
If I understand the issue correctly, then this can be fixed by adjusting your mainArea style object like so:
const mainArea={
/* position: 'fixed', Remove position fixed */
width: '80%',
bottom: '0%',
display: 'inline-block'
}
The fixed position basically has the effect of placing elements at a location on screen, relative to the client area of the window. This means that if you have multiple elements that share the same (default) coordinates, and are positioned with the fixed rule, then those elements will effectively overlap one another. This gives the appearance of only one element being visible at any given time.
For a working example, see this jsFiddle:
enter link description here
Hope that helps!