How to select multiple components at once with Emotion? - css

I have the following styled elements:
const LogoSide = styled.div``
const NavSide = styled.p``
const Nav = styled.nav`
position: absolute;
right: 0;
top: 0;
width: 100%;
${LogoSide} {
width: 200px; // <-- How to combine those ?
};
${NavSide} {
width: 200px; // <-- How to combine those ?
}
`
And I would like to be able to use the same CSS properties for multiple elements, something like this :
const Nav = styled.nav`
position: absolute;
right: 0;
top: 0;
width: 100%;
${LogoSide} ${NavSide} {
width: 200px;
};
`
Is there a way to achieve this ?
I looked at the docs but could not find what i'm looking for.

Just put a comma between the components and it will work. You won't find it in the documentation is works just like in plain CSS when you combine classes. But for correct work you need to use Macro.
All of Emotion's Babel Macros are available by adding /macro to the end of the import you'd normally use. Docs
import styled from "#emotion/styled/macro";
const NavSide = styled.p`
border: 2px solid red;
`;
const LogoSide = styled.div`
border: 2px solid red;
`;
const Nav = styled.nav`
position: absolute;
right: 0;
top: 0;
width: 100%;
${LogoSide}, ${NavSide} {
width: 200px;
}
`;

You can use the css prop to help with creating reusable, composable styles.
Note that you'll need to configure the JSX import source using this technique, so be sure to read the documentation I linked above.
If you're using create-react-app, and you don't feel confident after reading the docs, then you can probably just add /** #jsxImportSource #emotion/react */ at the top of each of your modules which have components that use the prop to take care of it.
Using it is pretty straightforward:
/** #jsxImportSource #emotion/react */
import {css} from '#emotion/react';
// Use this in any element's `css` prop:
const bold = css`
font-weight: bold;
`;
const red = css`
color: red;
`;
const boldRedUppercase = css`
${bold};
${red};
text-transform: uppercase;
`;
export function Component () {
return (
<p>
<span css={bold}>This</span> is bold and <span css={[bold, red]}>this</span> is bold red, and <span css={boldRedUppercase}>this</span> is bold red uppercase!
</p>
);
}

Related

Understanding css helper function in styled components

Styled components has a helper css function. But I don't understand when should I used it.
For example this is their example where they use it:
import styled, { css } from 'styled-components'
const complexMixin = css`
color: ${props => (props.whiteColor ? 'white' : 'black')};
`
const StyledComp = styled.div`
/* This is an example of a nested interpolation */
${props => (props.complex ? complexMixin : 'color: blue;')};
`
But if we take similar example from docs here they don't use it:
const Button = styled.button`
/* Adapt the colors based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
Their description is also not clear and is confusing me:
A helper function to generate CSS from a template literal with
interpolations. You need to use this if you return a template literal
with functions inside an interpolation due to how tagged template
literals work in JavaScript.
Can someone help explain why we need it?
PS this answer also doesn't use it
I use the css function when I create variants for a component:
that is variant switcher:
const sizeVariant: Record<NonNullable<StyledLogoProps['size']>, ReturnType<typeof css>> = {
small: css`
width: 44px;
height: 44px;
`,
regular: css`
width: 93px;
height: 93px;
`,
};
that is component was created by 'styled-components':
interface StyledLogoProps {
size: 'small' | 'regular';
}
export const StyledLogo = styled.div<StyledLogoProps>`
${({ size }) => sizeVariant[size]}
`;
and this is the use in the react:
<>
<StyledLogo size="regular" />
<StyledLogo size="small" />
</>
quite a useful thing

How to style a reusable component including two divs, only for one of two parent components?

I have two different components that they look like columns (QuantityComponent and CalendarViewComponent) and they will store different data, but they will have inside (one each) a horizontal line that moves when the time change (GreenCurrentTimeLine component), as the same reusable component. Now the greenLine is a single component and has one dot at the beginning, but I want that dot only for the QuantityComponent and not present for the CalendarViewComponent. The GreenCurrentTimeLine component is divided by two styledComponents named: GreenLine and Dot. Now the question, considering the code below:
const GreenCurrentTimeLine = ({ index, hours, minutes }) => {
// consts
const ratio = minutes / 60;
const topValue = 54 * ratio; // 54 in the height of the GreenLine Div
return (
<>
{index === hours && (
<GreenLine
index={index}
hours={hours}
minutes={minutes}
style={{
top: `${topValue}px`,
}}
>
<Dot></Dot>
</GreenLine>
)}
</>
);
};
const GreenLine = styled.div`
background-color: #02b396;
height: 2px;
width: 100%;
position: absolute;
top: 54px;
z-index: 98;
position: relative;
display: flex;
align-items: center;
`;
const Dot = styled.div`
background-color: #02b396;
position: absolute;
left: 10px;
border-radius: 20px;
width: 10px;
height: 10px;
z-index: 99;
left: -1px;
`;
How do I say, use the <Dot></Dot> styledComponent only when the parent component, in this case QuantityComponent is present and not for CalendarViewComponent? I also thought to go to the say something in the CalendarViewComponent and apply the style of the GreenCurrentTimeLine but without <Dot></Dot> but I am not sure how to tackle this. The current view of my page is this:
And what I want is this:
Since your components QuantityComponent and CalendarViewComponent both contain GreenCurrentTimeLine, you could possibly add a prop to your GreenCurrentTimeLine to take a boolean showDot and you could dynamically display or hide the Dot component like this
{ showDot && <Dot/>}

Choppy height CSSTransition in react with multiple items

Goal: I'm getting really frustrated and have been scavenging through posts for hours now and can't find any real solution. I'm trying to animation a table row's height to expand/collapse when clicked on. I figured there should be an easy enough solution... Boy was I wrong.
Ideally since each row is going to store quite a bit of extra data, I'd like to load in the data, expand that single row, then remove the data when it's collapsed.
Problem:
I managed to get the animation working great!! I was so excited, only to find out that when I have more than ~3 rows, it gets extremely choppy/laggy. I can't find any fix what so ever.
Here is a sandbox https://codesandbox.io/s/0ql5vp13qv or if you wanted to see the code that manages the animation my code now:
import React, { Component } from 'react'
import { CSSTransition } from 'react-transition-group';
import Icon2 from '../Icon';
import * as Styled from './styles'
export default class CourseRow extends Component {
state = {switched: false};
selectCourse = () => {
this.setState(({switched}) => ({switched: !switched}));
}
render() {
return (
<Styled.Row onClick={this.selectCourse}>
<Styled.InnerRow>
<Styled.Field light>CSCI 1000</Styled.Field>
<Styled.Field>Computer Science 1</Styled.Field>
<Styled.Field>Explore algorithms and data structures that...</Styled.Field>
<Styled.Field><Styled.DropdownButton ><Icon2 icon="up_arrow"/></Styled.DropdownButton></Styled.Field>
</Styled.InnerRow>
<CSSTransition
classNames="fade"
timeout={300}
key={this.props.Title}
in={this.state.switched}
unmountOnExit
>
<div>
test<br/>test<br/>test<br/>
</div>
</CSSTransition>
</Styled.Row>
);
}
}
import styled from 'styled-components';
import Button from 'components/Button';
const columns = "1.5fr 2.5fr 5fr 3fr";
export const DropdownButton = styled(Button)`
background-color: blue;
`;
export const InnerRow = styled.div`
display: grid;
grid-template-columns: ${columns};
grid-column-gap: 1rem;
grid-row-gap: 0;
padding-top: ${props => props.heading ? '2.25rem' : '1.5rem'};
padding-bottom: ${props => props.heading ? '2rem' : '1.5rem'};
padding-left: 0.75rem;
padding-right: 0.75rem;
border-top: ${props => props.heading ? 'none' : '1px solid #E8E8E8'};
cursor: ${props => props.heading ? 'default' : 'pointer'};
`;
export const Row = styled.div`
&:hover {
background-color: #f7faff;
color: #1873EA;
}
& .fade-enter {
overflow-y: hidden;
max-height: 0;
}
& .fade-enter-active {
max-height: 200px;
transition: all 200ms ease;
}
& .fade-exit {
max-height: 200px;
}
& .fade-exit-active {
max-height: 0;
overflow-y: hidden;
transition: all 200ms ease;
}
`;
export const Field = styled.span`
display: flex;
align-items: center;
font-size: 14px;
font-weight: ${props => props.light ? '400' : '300'};
&:last-of-type {
justify-content: flex-end;
}
`;
I thought maybe adding key's to the elements/transitions would help however still nothing...
I took videos of the comparisons so you can see what I'm talking about it.
This is with 1 row:
https://www.youtube.com/watch?v=E6Db7nuqlgk&feature=youtu.be
This is with ~6 rows:
https://www.youtube.com/watch?v=Troba8eIqKA&feature=youtu.be

Share the same styles between two types of component with React Styled Components

I would like to apply exactly the same styles to a styled input element and a styled select element.
Currently, I'm using string interpolation to do this:
const styles = `
background-color: white;
width: 100%;
border: 0 solid transparent;
border-radius: 10px;
margin-bottom: 20px;
padding-top: 5px;
padding-bottom: 5px;
font-size: 1.2rem;
`
const Select = styled.select`${styles}`
const Input = styled.input`${styles}`
Is there a better way of doing this which doesn't involve using a 'raw' string? The disadvantage of using the raw styles string is that Visual Studio Code doesn't syntax-highlight it:
You have few options here:
css helper function:
const styles = css`
background-color: white;
// ...
`;
const Select = styled.select`${styles}`;
const Input = styled.input`${styles}`;
"as" polymorphic prop (added in v4):
<Select as="input">
...
</Select>
withComponent method (candidate for deprecation):
const Select = styled.select`
background-color: white;
// ...
`;
const Input = Select.withComponent('input');
You can use the css tagged template literal:
import styled, { css } from "styled-components";
const styles = css`
background-color: white;
width: 100%;
`;
const Select = styled.select`${styles}`;
const Input = styled.input`${styles}`;
That should get properly syntax highlighted (haven't tested).

How to extend(inherit) a global CSS class from within a styled-components

Is it possible to inject a global CSS class into a styled-component?
Similar to extending in LESS using &:extend or #extend in SASS.
This code doesnt apply the globalCSS styles:
const extendedComponent = styled.div`
&:extend(.globalClass) // I want this component to inherit all styles of .globalaCSS
.otherStyles {
...
}
`
The globalClass does exits and even applies styles when used inline:
extendedComponent(className="globalCSS)
you can just add the "class name" to your element,
but if you really want to inherit or extend.
Use their example as reference:
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// We're extending Button with some extra styles
const TomatoButton = Button.extend`
color: tomato;
border-color: tomato;
`;
docs link:
https://www.styled-components.com/docs/basics#extending-styles
Use attrs: https://styled-components.com/docs/api#attrs
const extendedComponent = styled.div.attrs({
className: 'globalCssClassThatIWantToExtend'
})`
.otherStyles {
/*...*/
}
`;
The attrs function can also accept a props function:
.attrs(props => ({
/*...*/
}))

Resources