Reuse const object in react - reactjs

Say I would want to have the same codeblock in an object, with an addition:
const styles = {
styleA: `
background-color: red;
color: silver;
`,
styleB: `
background-color: green;
color: white;
`,
styleC: `
background-color: red;
color: silver;
font-size: 16px;
`
};
As you can see, styleA and styleC are similar, exept for the fontsize addition to styleC. How can I rewrite this with react (es6) so that
styleA = styleC + 'font-size: 16px'; ?
Or is there a better way of doing this altogether?

You can use the css helper from styled-components to help create styles and mixins from other readymade components.
For example, in your case:
const styleA = css`
background-color: red;
color: silver;
`;
const styleB = css`
background-color: yellow;
`;
const styles = {
styleC: css`
${styleA}
${styleB}
font-size: 32px;
`
};
const MainHeading = styled.h2`
${styles.styleC}
`;

you can have something like this.
const createStyle = (bgColor, color, fontSize) => {
return (
`
background-color: ${bgColor};
color: ${color};
${fontSize ? `font-size: ${fontSize}` : null}; //if there is fontSize, return it
`
);
}
thus:
const styles = {
styleA: createStyle('red', 'silver'),
styleB: createStyle('red', 'silver', '16px')
};

Related

How to inherit styles from another styled component and turning regular component into styled component at the same time?

I am using StyledComponents stying framework and This is my regular react component
const SelectButton = ({className,children, ...rest}) => {
return (
<select className = {className}{...rest}>
{children}
</select>
);
}
I want to turn this component into styled component by calling styled() function and for that purpose I have attached className prop to DOM element of my react component (SelectButton).
export const StyledSelectButton = styled(SelectButton);
But instead of putting the css in this styled component, I want to inherit from different styled component which is StyledButton.js, which has following css properties.
export const StyledButton = styled(Button).attrs(({ type }) => ({
type: type || "button",
}))
display: inline-block;
height: auto;
padding: 0.8rem 2rem;
border: none;
border-radius: 6px;
font-weight: 500;
font-size: 1.6rem;
text-decoration: none;
text-transform: capitalize;
cursor: pointer;
overflow: hidden;
background-color: ${({ primary }) => (primary ? "#646ff0" : "#cccdde")};
color: ${({ primary }) => (primary ? "white" : "#646681")};
.__select {
color: #585858;
font-family: Poppins;
padding: 1rem;
border: none;
background-color: #cccdde;
width: 150px;
cursor: pointer;
};
How can I achieve that?
I have tried doing this way , but I am repeating my code.
export const StyledSelectButton = styled(SelectButton)
display: inline-block;
height: auto;
padding: 0.8rem 2rem;
border: none;
border-radius: 6px;
font-weight: 500;
font-size: 1.6rem;
text-decoration: none;
text-transform: capitalize;
cursor: pointer;
overflow: hidden;
background-color: ${({ primary }) => (primary ? "#646ff0" : "#cccdde")};
color: ${({ primary }) => (primary ? "white" : "#646681")};
&__select {
color: #585858;
font-family: Poppins;
padding: 1rem;
border: none;
background-color: #cccdde;
width: 150px;
cursor: pointer;
}
You can do something like this,
import styled, {css} from "styled-components";
import { StyledButton } from './Button';
const style = css`
color: #585858;
font-family: Poppins;
padding: 1rem;
border: none;
background-color: #cccdde;
width: 150px;
cursor: pointer;
`;
Using function declaration method:
export function StyledSelectButton({ className, children, ...rest }){
return (
<select className={className} {...rest}>
{children}
</select>
);
};
To turn this component into a styled component, pass it to the styled() function.
StyledSelectButton = styled(StyledButton).attrs((props) => ({
as: "select"
}))`
${style}
`;

Renaming button.jsx makes entire react web unable to compile

i've been tasked with renaming a Button.jsx file for my react web app into 'v2_button.jsx' however when I do so it makes the entire web app unable to compile as there are many times where 'Button' is called but not 'v2_button'. My question is how do I make it so that I can update the name of the jsx file and update every instance where the original Button.jsx is called into 'v2_button'?
Button.jsx file:
import styled from 'styled-components';
const Button = styled.Button`
display: flex;
align-items: center;
justify-content: center;
height: 50px;
width: 300px;
background: ${(props) => {
let bg;
if (props.disabled) {
bg = props.theme.colors.disabled.background;
} else if (props.color === 'secondary') {
bg = props.theme.colors.primary.main;
} else if (props.color === 'danger') {
bg = props.theme.colors.danger.main;
} else {
bg = props.theme.colors.white;
}
return bg;
}};
font-family: ${(props) => props.theme.font.font3.new};
font-size: 18px;
font-weight: ${(props) => props.theme.fontWeight.heavy_900};
letter-spacing: 0.105em;
color: ${(props) => {
let fontColor;
if (props.color === 'secondary') {
fontColor = props.theme.colors.textPrimary.light;
} else {
fontColor = props.theme.colors.textPrimary.purple.dark;
}
return fontColor;
}};
padding: ${(props) => {
let padding;
if (props.size !== 's') {
padding = '0 39px';
}
return padding;
}};
border-radius: 40px;
border: ${(props) =>
props.disabled
? `1px solid ${props.theme.colors.disabled.border}`
: 'none'};
white-space: nowrap;
transition: background ${(props) => props.theme.animations.short} linear;
:hover {
background: ${(props) => {
let bg;
if (props.disabled) {
bg = props.theme.colors.disabled.background;
} else if (props.color === 'secondary') {
bg = props.theme.colors.primary.light;
} else if (props.color === 'danger') {
bg = props.theme.colors.danger.light;
} else {
bg = props.theme.colors.secondary.light;
}
return bg;
}};
cursor: pointer;
}
`;
export default Button;
Example where Button is called:
import styled from 'styled-components';
import Button from "../../Button";
export const Wrapper = styled.div`
display: flex;
flex-direction: column;
`;
export const Container = styled.div`
display: flex;
flex-direction: column;
`;
export const NameContainer = styled.div`
width: 100%;
display: flex;
align-content: space-between;
flex-direction: row;
font-style: normal;
font-weight: ${(props) => props.theme.fontWeight.med_500};
font-size: ${(props) => props.theme.fontSize.med_16};
`;
export const List = styled.div`
display: flex;
`;
export const FieldLabel = styled.label`
height: 36px;
color: ${(props) => props.theme.colors.white};
font-family: ${(props) => props.theme.font.font1.demi};
margin: 0 0 25px 0;
text-align: center;
font-style: normal;
font-weight: ${(props) => props.theme.fontWeight.heavy_800};
font-size: ${(props) => props.theme.fontSize.large_28};
line-height: 50px;
`;
export const Selected = styled.label`
width: 158px;
height: 40px;
margin: 0 10px 20px 10px;
padding-top: 10px;
padding-left: 20px;
font-size: ${(props) => props.theme.fontSize.med_20};
color: ${(props) => props.theme.colors.white};
font-family: ${(props) => props.theme.font.font1};
caret-color: ${(props) => props.theme.colors.textSecondary.main};
:focus {
outline: none;
}
:focus-within {
box-shadow: inset 0px 0px 2px 1px
${(props) => props.theme.colors.textSecondary.main};
}
::placeholder {
color: ${(props) => props.theme.colors.textPrimary.light};
}
background: ${(props) => props.theme.colors.primary.main};
border: ${(props) => props.theme.borders.textSecondary};
border-radius: 4px;
`;
export const AddFieldButton = styled(Button)`
margin: 30px;
font-size: ${(props) => props.theme.fontSize.med_20};
color: ${(props) => props.theme.colors.textSecondary.main};
background: none;
width: 1px;
text-align: center;
`;
Compiling error I receive after changing Button.jsx to v2_button.jsx:
https://imgur.com/a/kC0AdmG
The best thing is to renamed all the imports.
But a 'workaround' to renaming all the imports could be:
Keep the Button.jsx
Instead of exporting the default implementation, import the implementation from v2_button.jsx file and re-export it from the Button.jsx file.
Thus, every import of Button.tsx will actually refer to the implementation of v2_button.tsx file.
Example:
Button_v1.tsx
const Button_v1 = () => (
<>Button v1</>
);
export default Button_v1;
Button_v2.tsx
const Button_v2 = () => (
<>Button v2</>
);
export default Button_v2;
Since all other files reference Button_v1 and you don't want OR can't update the imports, you could do this:
Button_v1.tsx (after)
import Button_v2 from "./Button_v2"; // <-- ADDED THIS
const Button_v1 = () => (
<>Button v1</>
);
// export default Button_v1; // <-- COMMENTED THIS
export default Button_v2; // <-- ADDED THIS

Implementing animation when removing Toast

I have a working ToastList that enables me to click a button multiple times and generate a toast each time. On entry, I have an animation, but when I remove the toast, I do not get an animation. I am using Typescript and functional components.
My component is as follows:
import React, { useCallback, useEffect, useState } from 'react';
import * as Styled from './Toast.styled';
export interface ToastItem {
id: number;
title: string;
description: string;
backgroundColor: string;
}
export interface ToastProps {
toastList: ToastItem[];
setList: React.Dispatch<React.SetStateAction<ToastItem[]>>;
}
export default function Toast(props: ToastProps) {
const deleteToast = useCallback(
(id: number) => {
const toastListItem = props.toastList.filter((e) => e.id !== id);
props.setList(toastListItem);
},
[props.toastList, props.setList]
);
useEffect(() => {
const interval = setInterval(() => {
if (props.toastList.length) {
deleteToast(props.toastList[0].id);
}
}, 2000);
return () => {
clearInterval(interval);
};
}, [props.toastList, deleteToast]);
return (
<Styled.BottomRight>
{props.toastList.map((toast, i) => (
<Styled.Notification
key={i}
style={{ backgroundColor: toast.backgroundColor }}
>
<button onClick={() => deleteToast(toast.id)}>X</button>
<div>
<Styled.Title>{toast.title}</Styled.Title>
<Styled.Description>{toast.description}</Styled.Description>
</div>
</Styled.Notification>
))}
</Styled.BottomRight>
);
}
And my styling is done using styled-components and is as follows:
import styled, { keyframes } from 'styled-components';
export const Container = styled.div`
font-size: 14px;
position: fixed;
z-index: 10;
& button {
float: right;
background: none;
border: none;
color: #fff;
opacity: 0.8;
cursor: pointer;
}
`;
const toastEnter = keyframes`
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
`;
export const BottomRight = styled(Container)`
bottom: 2rem;
right: 1rem;
`;
export const Notification = styled.div`
width: 365px;
color: #fff;
padding: 15px 15px 10px 10px;
margin-bottom: 1rem;
border-radius: 4px;
box-shadow: 0 0 10px #999;
opacity: 0.9;
transition .1s ease;
animation: ${toastEnter} 0.5s;
&:hover {
box-shadow: 0 0 12px #fff;
opacity: 1;
}
`;
export const Title = styled.p`
font-weight: 700;
font-size: 16px;
text-align: left;
margin-bottom: 6px;
`;
export const Description = styled.p`
text-align: left;
`;
When I click a button, I just add an element to the state list, like:
toastProps = {
id: list.length + 1,
title: 'Success',
description: 'Sentence copied to clipboard!',
backgroundColor: '#5cb85c',
};
setList([...list, toastProps]);
My component is rendered like:
<Toast toastList={list} setList={setList}></Toast>
I would like to add animation when a toast exits, but do not know how. I have tried changing the style according to an additional prop I would send to the styled components, but this way all the toasts animate at the same time. My intuition is that I should use useRef(), but I am not sure how. Thanks in advance for any help you can provide.

Target another styled component on hover not working

I need help with hover in styled components. If hover is in component and affect that component, that work. But if hover is in component and affect another component, not working.
DropDownContent not show on DropDownLi:hover.
export const DropDownLi = styled(StyledLi)`
display: inline-block;
&:hover {
color: red;
}
`;
export const DropDownContent = styled.div`
display: none;
position: absolute;
min-width: 160px;
z-index: 1;
${DropDownLi}:hover & {
display: block;
}
/* not show on hover */
`;
Not working in next example.
export const DropDownLi = styled(StyledLi)`
display: inline-block;
&:hover ${DropDownContent} {
color: red;
}
`;
<StyledUl>
<DropDownLi>$ Currency</DropDownLi>
<DropDownContent>dvsdv</DropDownContent>
<DropDownLi>English</DropDownLi>
<StyledLi>Login</StyledLi>
</StyledUl>
Try to remove &:
export const DropDownContent = styled.div`
display: none;
position: absolute;
min-width: 160px;
z-index: 1;
${DropDownLi}:hover {
display: block;
}
/* not show on hover */
`;
I found a solution for this problem. The code that works is below.
<StyledUl>
<DropDownLi>
$ Currency
<DropDownContent>dvsdv</DropDownContent>
</DropDownLi>
</StyledUl>
export const StyledLi = styled.li`
float: left;
cursor: pointer;
`;
export const DropDownContent = styled.div`
display: none;
position: absolute;
min-width: 160px;
z-index: 1;
`;
export const DropDownLi = styled(StyledLi)`
display: inline-block;
`;
export const StyledUl = styled.ul`
list-style-type: none;
margin: 0;
overflow: hidden;
color: black;
${DropDownLi}:hover > ${DropDownContent} {
display: block;
}
`;

How to interpolate a CSS property in styled-components?

I am trying to create a Styled Component which can receive a prop for the type of border. The Section can either have border-top or border-bottom based on the prop passed to it.
Here is the code
type TSectionBorder = 'top' | 'bottom';
<Section edge="top">
{children}
</Section>
How can I achieve this in styled components, something along these lines -
const Section = styled.section<{edge: TSectionBorder}>`
border-${edge}: 1px solid black;
`;
const Section = styled.section<{edge: TSectionBorder}>`
${({ edge }) => `border-${edge}: 1px solid black`};
`;
I deconstructed the props just to keep your syntax, however, this is one way to do so. They also have a css helper to look into.
Documentation:
https://styled-components.com/docs/basics#passed-props
https://styled-components.com/docs/api#css
First you will import:
import styled from 'styled-components';
Here is the function ButtonBlock:
function ButtonBlock(props) {
return (
<BtnBlock type="primary" {...props}>
{props.children}
</BtnBlock>
);
}
const handleColorType = (color) => {
switch (color) {
case 'grey':
return '#D4D5D6';
default:
return '#6633CC';
}
};
const hoverColorType = (color) => {
switch (color) {
case 'grey':
return '#6a7581';
default:
return '#99CC00';
}
};
Styled Component:
const BtnBlock = styled(Button)`
&& {
max-width: none;
width: 100%;
text-align: center;
display: block;
font-size: 14px;
font-weight: 600;
line-height: 50px;
height: 50px;
text-transform: uppercase;
border-radius: 0;
border: 0px;
padding: 0;
background: ${({ color }) => handleColorType(color)};
&[disabled] {
background-color: #c6c6c6;
}
:hover {
background-color: ${({ color }) => hoverColorType(color)};
}
&[disabled]:hover {
background-color: #c6c6c6;
}
}
`;

Resources