using styled-component in react.js - reactjs

I am first time using styled-component with react.js. My code is in Abc.js and in styles.js I have styled-component code.
export const StyledPopopDiv = styled.div`
width: 200px;
height: 100px;
background: #cecece;
position: fixed;
z-index: 200;
left: ${state => state.pos.x}px;
top: {state.pos.y}px;
`;
StyledPopopDiv.displayName = 'StyledPopopDiv';
I am trying to pass state.pos.x in styled-component, but this is giving error Cannot read property 'x' of undefined.
I don't know what to do. Please help.

You should first of all pass the state as a prop to the styled component example :
// Abc.js
<StyledPopopDiv pos={posState} />
So you can use this prop on your styled component definition as below:
//styled.js
export const StyledPopopDiv = styled.div`
width: 200px;
height: 100px;
background: #cecece;
position: fixed;
z-index: 200;
left: ${ props => props.pos.x}px; // outputs '10px' in this example
top: ${ props => props.pos.y}px; // outputs '20px' in this example
`;
A better way/tip is to use destruction/spread operator example :
export const StyledPopopDiv = styled.div`
width: 200px;
height: 100px;
background: #cecece;
position: fixed;
z-index: 200;
left: ${ ({ pos }) => pos.x}px;
top: ${ ({ pos }) => pos.y}px;
`;

it should be props instead of state, assuming you're using StyledPopupDiv in Abc.js render.

interface StyledPopupDivProps{
x:string
}
export const StyledPopopDiv =
styled<LinkProps, "div">("div")`
margin: ${props=>props.x}
`;
and pass
<StyledPopupDiv x={intendedValue}/>
where its being used.

Related

React Styled Components styles do not apply properly

I have problem with understanding how cascade style sheet algorithm works with React library styled-component. I have created Todo element with 3 fields, and I gave each its own class , like TodoTitle, TodoDescription and TodoAction - so that div with different class will have diferent width. Yet width property does not apply to class, even though it is flex. Other properties like red backgound did aplied.
import React from 'react';
import { Todo } from '../../types/Todo';
import { StyledTodoItem } from './TodoItem.styles';
type Props = {
todo: Todo;
index: number;
};
export const TodoItem: React.FC<Props> = ({ todo, index }) => {
return (
<StyledTodoItem bg={index % 2 === 0 ? 'even' : 'odd'}>
<div className="todoTitle">
<h1>{todo.title}</h1>
</div>
<div className="todoDescription">
<h1>{todo.text}</h1>
</div>
<div className="todoAction">
<button type="button">Hello</button>
</div>
</StyledTodoItem>
);
};
import styled from 'styled-components';
type Props = {
bg: 'even' | 'odd';
};
export const StyledTodoItem = styled.div<Props>`
width: 100%;
display: flex;
background: ${({ bg }) => (bg === 'odd' ? 'white' : '#ededed')};
height: ${({ theme }) => theme.todoTable.elementMinHeight};
border-right: 1px solid ${({ theme }) => theme.palette.borderColor};
.todoTitle {
display: flex;
justify-content: center;
padding: 10px 20px;
width: 150px;
background: red;
}
`;
If , instead of .todoTitle I paste div , width will be applied, but for every element.
Please, explain how to fix this and any similar problem like this that might arise.
Thanks in advance.
Thanks for comments. So I checked out few tutorials and it seems that best practice is to avaid as possible html tags and to change them for custom styled components as provided in example. Also nested components do lead to lots of bugs, or so it seems.
return (
<>
<TodoItemWrapper bg={index % 2 === 0 ? 'even' : 'odd'}>
<TodoTitle>
<TextTitle>{todo.title}</TextTitle>
</TodoTitle>
<TodoDescr>
<TextDescr>{todo.text}</TextDescr>
</TodoDescr>
<TodoAction>
<Button>View</Button>
<Button>Edit</Button>
<Button>Delete</Button>
</TodoAction>
</TodoItemWrapper>
)
export const TodoItemWrapper = styled.div`
width: 100%;
display: flex;
background: ${({ bg }) => (bg === 'odd' ? 'white' : '#ecfcff')};
height: fit-content;
`;
export const TodoText = styled.div`
display: flex;
justify-content: flex-start;
align-items: center;
padding: 0 20px;
`;
export const TodoTitle = styled(TodoText)`
width: ${({ theme }) => theme.todoTable.title};
border-right: 1px solid ${({ theme }) => theme.palette.borderColor};
`;

Flexbox Container with Child in it with position absolute

I'm new to React, and I'm currently building a slideshow on my website. Basically, I have a left arrow, the slide content, then the right arrow in my flexbox for my slideshow. However, to make my arrows on the edges of the screen I needed to use position absolute, which messes up my flexbox container. I figured out that absolutely positioned child items don't count in the flexbox of the parent, but is there a way around that? As of now, my image is overlapping with the left arrow. Thanks, and sorry if this is poorly written since I'm very new to this all. Code will be below.
Image: [1]: https://i.stack.imgur.com/PkL3Z.jpg
import { ArrowBackIosOutlined, ArrowForwardIosOutlined } from '#material-ui/icons'
import React from 'react'
import styled from 'styled-components'
import grocery from '../assets/grocery.png'
const Container = styled.div`
width: 100%;
height: 100vh;
display: flex;
position: relative;
`
const Arrow = styled.div`
width: 50px;
height: 50px;
background-color: #fff7f7;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
bottom: 0;
left: ${props => props.direction === "left" && "10px"};
right: ${props => props.direction === "right" && "10px"};
margin: auto;
cursor: pointer;
opacity: 0.5;
`
const Wrapper = styled.div`
height: 100%;
`
const Slide = styled.div`
width; 100vw;
height: 100vh;
display: flex;
align-items: center;
`
const ImageContainer = styled.div`
height: 100%;
flex: 1;
margin-top: 50px;
`
const Image = styled.img`
height: 50%;
`
const InfoContainer = styled.div`
flex: 1;
`
const Slider = () => {
return (
<Container>
<Arrow direction="left">
<ArrowBackIosOutlined/>
</Arrow>
<Wrapper>
<Slide>
<ImageContainer>
<Image src={https://images.unsplash.com/photo-1481349518771-20055b2a7b24?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8M3x8cmFuZG9tfGVufDB8fDB8fA%3D%3D&w=1000&q=80}/>
</ImageContainer>
</Slide>
</Wrapper>
<Arrow direction="right">
<ArrowForwardIosOutlined/>
</Arrow>
</Container>
)
}
export default Slider

React, styled-components - style inner styled-component

I have a demo
https://stackblitz.com/edit/react-ts-jvbeh7
It's a simple react app using styled-components
I'm passing a prop desktop
<Block desktop>
<InnerBlock />
</Block>
And I'm trying to style the inner style component when the prop is there
const Block = styled.div<IProps>`
background: red;
height: 200px;
width: 200px;
${InnerBlock} {
margin: ${p => p.desktop ? '10px' : null};
}
`;
If I try this I get an error on ${InnerBlock}
Block-scoped variable 'InnerBlock' used before its declaration.
Is it possible to style an inner styled-component <InnerBlock /> when there is a prop on the parent
Found the correct way.
const Block = styled.div<IProps>`
background: red;
height: 200px;
width: 200px;
${InnerBlock} {
${(p) =>
p.desktop &&
css`
width: 50px;
height: 50px;
background: yellow;
`};
}
`;

By using styled components, how can I add some opacity to a background color that comes from an external prop?

This is my code, below it you will find the question
import { printIntrospectionSchema } from 'graphql';
import React, { useContext, useState } from 'react';
import styled from 'styled-components';
import { Context } from '../../../Context/Context';
// DATA
function DurationIntervalSelection() {
const tabsData = [
{
text: 'Duration',
},
{
text: 'Interval',
},
];
// STATE
const [selectedIndex, setselectedIndex] = useState(0);
const { selected } = useContext(Context);
console.log(selected.color, 'selected color');
// FUNCTION
const handleIndexSelection = (index) => {
setselectedIndex(index);
};
return (
<Container>
<TabsContainer backgroundColor={selected.backgroundColor}>
<Indicator
backgroundColor={selected.color}
style={{
left: `${selectedIndex * 50}%`,
}}
/>
{tabsData.map((tab, index) => {
return (
<Tab
style={{
color: selectedIndex === index ? 'white' : 'black',
}}
onClick={() => handleIndexSelection(index)}
key={tab.text}
>
<p style={{ zIndex: 100 }}>{tab.text}</p>
</Tab>
);
})}
</TabsContainer>
</Container>
);
}
const Container = styled.div`
margin-top: 10px;
border: 1px solid #f2f2f7;
border-radius: 10px;
box-sizing: border-box;
padding: 10px 0;
`;
const Indicator = styled.div`
width: 50%;
position: absolute;
/* z-index: -1; */
height: 100%;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
background-color: ${(props) => props.backgroundColor};
`;
const TabsContainer = styled.div`
display: flex;
width: 100%;
justify-content: space-between;
position: relative;
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);
border-radius: 20px;
overflow: hidden;
background-color: ${(props) => props.backgroundColor};
`;
const Tab = styled.div`
display: flex;
width: 50%;
justify-content: center;
cursor: pointer;
align-items: center;
padding: 10px;
`;
export default DurationIntervalSelection;
As you can see from the above code i pass backgroundColor as a prop that takes some color from a React state.
<TabsContainer backgroundColor={selected.backgroundColor}>
And in the styled component I do:
background-color: ${(props) => props.backgroundColor};
But my question is, since i need to follow a design made by a colleague, how do i add opacity to the above background color without affecting the opacity of the whole div: TabsContainer. I mean, if i add on the TabsContainer -> opacity: 0.3 - for instance the whole TabsContainer div will be affected by the opacity, including the Tab div, while what I want is just to change opacity on the color itself so i do not affect any child. I hope it makes sense. Is there a way?
Okay if your hex value comes from your backend, you can either create your own javascript function that converts hex to rgba or you can use a library like polished. They have a function named rgba that converts a hex string and adds an alpha value to it
import { rgba } from 'polished'
const div = styled.div`
background: ${(props) => rgba(props.backgroundColor, 0.3)};
`
Or you can apply it directly to the prop
<TabsContainer backgroundColor={rgba(selected.backgroundColor, 0.3)}>
Example CodeSandbox.
Since the data from the backend where right but I could not find a solution I have realised that I could have added to
<TabsContainer backgroundColor="rgba(255,255,255, 0.5)">
A background color on the white notes with opacity so to emphasise the color behind it in a lighter way

Styled components on hover change img src attribute

I have a styled component as below. It is a google social login button imgGoogleLogin is a path loaded by webpack.
I want to change the src attribute to another src when it is on hover.
Is there any way to achieve this? Thanks a lot
const GoogleLoginButton = styled.img.attrs({
src: imgGoogleLogin,
})`
width: 190px;
height: 45px;
&:hover {
cursor: pointer;
}
`;
I wanted to achieve something similar but I found that styled components cannot explicitly do this. I had to do it this way, ie create two components one hidden and when the parent is hovered I unhide it and hide the other one. Seems hacky but better than using e.setAttribute I think.
const GoogleLoginButton = styled.img.attrs(
props => ({'src': props.img})
)`
display: inline;
width: 190px;
height: 45px;
&:hover {
cursor: pointer;
}
`;
const GoogleLoginButtonHover = styled.img.attrs(
props => ({'src': props.img})
)`
display: none;
width: 190px;
height: 45px;
&:hover {
cursor: pointer;
}
`;
const GoogleLoginButtonParent = styled.div`
&:hover ${GoogleLoginButtonHover} {
display: inline;
}
&:hover ${GoogleLoginButton} {
display: none;
}
`;
In your render you use it like this:
<GoogleLoginButtonParent>
<GoogleLoginButton
img = {props.img}
/>
<GoogleLoginButtonHover
img = {props.imgHover}
/>
</GoogleLoginButtonParent>
You can achieve this with pure CSS:
By replacing your img tag with a div and setting its CSS as follow:
div {
background: url('to_first_image');
}
div:hover {
background: url('to_second_image');
}
If you rather keep your img tag and use some JS:
onHover = (e) => {
e.setAttribute('src', 'source_to_first_img');
}
onUnhover = (e) => {
e.setAttribute('src', 'source_to_second_img');
}
Credit to this answer : https://stackoverflow.com/a/18032363/10449875

Resources