How to concate two styled div using props in styled component - css

I want to concate two div in a styled component using prop value
import styled, { css } from 'styled-components';
interface Props {
isValue: boolean;
}
export const sample = css`
display: flex;
justify-content: space-between;
padding: 20px 0 0 16px;
`;
export const sample1 = styled.div<Props>`
border-bottom: 1px solid red;
padding-bottom: 20px;
overflow: hidden;
${(props) => (props.isValue ? sample : '')};
`;
I have tried the above code , it is not working
Requirement: If isValue is true sample should get added to sample1 as shown below
so sample1 should contain
padding-bottom: 12px;
overflow: hidden;
display: flex;
justify-content: space-between;
padding: 12px 0 0 16px;
If isValue is false it should show only sample1 values

Related

Is there any way i can style an element based on other elements state using styled components?

I just started using styled-components and currently converting all my css into styled components in a project. But i am not sure how to achive some things and i could not find anything relevant in documentation too. I would like to know if the following problem is achievable using this approach in styled-components.
I have two checkboxes(radio input), if its checked i want to style a span element(Dot component in this case).
<div className="acc__type">
<RadioInput type="radio" name="accType" id="dot-1" required />
<RadioInput type="radio" name="accType" id="dot-2" required />
<Category>
<AccTypeLabel for="dot-1">
<Dot></Dot>
<AccTypeTitle>Student</AccTypeTitle>
</AccTypeLabel>
<AccTypeLabel for="dot-2">
<Dot></Dot>
<AccTypeTitle>Staff</AccTypeTitle>
</AccTypeLabel>
</Category>
</div>
here are my styled components
export const RadioInput = styled.input`
display: none;
`;
export const Category = styled.div`
display: flex;
width: 80%;
margin: 15px 0;
justify-content: space-between;
`;
export const AccTypeLabel = styled.label`
display: flex;
align-items: center;
`;
export const AccTypeTitle = styled.div`
font-size: 20px;
font-weight: 500;
`;
export const Dot = styled.span`
height: 18px;
width: 18px;
background: #d9d9d9;
border-radius: 50%;
margin: 10px;
border: 5px solid transparent;
transition: all 0.3s ease;
//here iam trying to change this components styles based on <RadioInput/> components state.
#dot-1:checked & {
border-color: #d9d9d9;
background: #9b59b6;
}
`;
in css i did this and it worked
#dot-1:checked ~ span,
#dot-2:checked ~ span {
border-color: var(--sub-grey);
background: var(--main-purple);
}
/* <Dot/> component was span here */
any help!
In Styled Components it's an ever so slightly different syntax, the reason is that it hashes the class names in a way that makes it difficult to target them. So this is how you do it in a way that ensures SC knows what's what:
const dot1StyledComponent = styled.div`
...
`;
${ dot1StyledComponent }:checked ~ span {
border-color: #d9d9d9;
background: #9b59b6;
}

Shrink of inside label in a flex item

Hello everybody I need a help. The issue is the following: I have flex-container with tabs (number of tabs can be different as they are passed as array in React component).
Each tab contains label and span with a number. By initial conditions for this task, label should not be shorter than 3 letters + "..." (we apply ellipsis for it).
As I have understood, there is no other solution but to do it manually from code (ch unit is based on symbol 0, that's why this approach gives inaccurate result). But let's move to key issue. Text in label can have different length, and we can have different number of tabs.
I need to set tabs in container (let it be restricted by max-width of 900px) as long as it possible. What does it mean: we set tabs with full length of label if it is possible, if not - label shrinks until it reaches min-width (6ch). If number of tabs is too large (all labels have min-width, but tabs exceed container), I will not render them at all. I am going to implement that with help of useLayoutEffect with checks of exceeding container there.
The main problem now is that spans overflow tabs, in other words labels have possibility to shrink, but instead of that other tabs start shrinking arising problems with span. I have tried to use grid with templates columns of 1fr width (number of columns I can set by passing length of array to styled component). That works, but I need to have by tabs aligned to left side (instead of that they would take all available space) and I have problems with extra empty space if label + gap + span < 1fr of container.
By this moment I have no solution, but to hardcode min-width of tab, but all of us understand that it is unacceptable (not to mention the fact that there can be 10,000, for example, in span). I am asking for any help. I would be the happiest person if I found solution.
I have attached images with demonstration of issue, code, and link to codeSandbox with example (there you can insert tabs in mock_data, change length of words).
CodeSandBox - https://codesandbox.io/s/gracious-dijkstra-61s9sp?file=/src/Component.jsx:0-1606
tabs have enough space
labels can shrink, but instead spans overflow tabs
import styled from "#emotion/styled";
const TabsList = styled.ul`
list-style: none;
display: flex;
justify-content: flex-start;
gap: 20px;
margin: 0;
margin-left: 20px;
width: 100%;
max-width: 900px;
background: yellowgreen;
/* because the first tab always will be "all" */
li:first-of-type label {
min-width: 20px;
}
`;
const singleNumPaddingStyles = "0 8px";
const KeywordTab = styled.li`
position: relative;
overflow: hidden;
display: flex;
align-items: center;
padding-bottom: 4px;
gap: 8px;
label {
display: block;
font-weight: 400;
line-height: 23px;
cursor: pointer;
user-select: none;
text-transform: capitalize;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:hover {
color: blue;
}
/* trying to set minimum 3char + ... */
min-width: 6ch;
}
span {
color: white;
line-height: 23px;
background-color: pink;
user-select: none;
padding: ${({ singleNum }) =>
singleNum ? singleNumPaddingStyles : "0 4px"};
border-radius: 4px;
}
`;
const Group = ({ label, number }) => (
<KeywordTab singleNum={number < 10}>
<label>{label}</label>
<span>{number}</span>
</KeywordTab>
);
export const View = ({ dictionaries }) => {
//logic (useLayoutEffect)
return (
<TabsList>
{dictionaries.map(({ label, total }, index) => (
<Group key={index} label={label} number={total} />
))}
</TabsList>
);
};
//very-very-very bad desicion: hardcode min-width
// of tab ~ 88px (53px for the first - it will always be "all")
I have achieved approximately desired behaviour by using grid instead of flexbox. On parent container i've started to set number of columns dynamically and give them width of minmax(min-content, max-content) (except for the first column as there is always expected element - All). Separate tab was rewritten to grid with 2 columns - (1fr min-content respectively)
import styled from "#emotion/styled";
import { useLayoutEffect, useRef, useState, forwardRef } from "react";
export const TabsListGrid = styled.ul`
margin: 0;
width: 100%;
display: grid;
list-style: none;
background: yellowgreen;
grid-gap: 20px;
grid-template-columns: min-content repeat(${({ columns }) => columns - 1}, minmax(min-content, max-content));
max-width: 712px;
li:first-of-type label {
min-width: min-content;
}
`
const singleNumPaddingStyles = "0 8px";
const KeywordTab = styled.li`
flex-shrink: 0;
position: relative;
overflow: hidden;
display: grid;
grid-template-columns: 1fr min-content;
align-items: center;
padding-bottom: 4px;
grid-gap: 8px;
label {
font-weight: 400;
line-height: 23px;
cursor: pointer;
user-select: none;
text-transform: capitalize;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:hover {
color: blue;
}
min-width: 6ch;
}
span {
color: white;
line-height: 23px;
background-color: pink;
user-select: none;
padding: ${({ singleNum }) =>
singleNum ? singleNumPaddingStyles : "0 4px"};
border-radius: 4px;
}
`;
const Group = forwardRef(({ label, number }, ref) => (
<KeywordTab ref={ref} singleNum={number < 10}>
<label>{label}</label>
<span>{number}</span>
</KeywordTab>
));
export const View = ({ dictionaries }) => {
const [visible, setVisible] = useState(true);
const tabsRef = useRef(null);
const tabRef = useRef([]);
useLayoutEffect(() => {
if (tabsRef?.current?.getBoundingClientRect().width < 500) {
setVisible(false);
} else {
setVisible(true);
}
console.log(tabRef);
}, []);
return (
<>
{visible && (
<TabsListGrid ref={tabsRef} columns={dictionaries.length}>
{dictionaries.map(({ label, total }, index) => (
<Group
ref={(el) => (tabRef.current[index] = el)}
key={index}
label={label}
number={total}
/>
))}
</TabsListGrid>
)}
</>
);
};

How to add a css property only when condition true using styled components and react?

i wanted to add border property only when isOpen condition true using styled components and react.
below is my code,
const Wrapper = styled.div<{ $isOpen: boolean }>`
border: 1px solid black;
display: flex;
`;
How to apply border only when isOpen is true?
could someone help me with this. thanks.
You can try like this:
const Wrapper = styled.div`
display: flex;
${({isOpen}) =>
isOpen &&
css`
{
border: 1px solid black;
}`
}
`;
Grabbing an example from the home page of styled-components:
import styled, { css } from 'styled-components'
const Button = styled.button`
background: transparent;
border-radius: 3px;
border: 2px solid palevioletred;
color: palevioletred;
margin: 0 1em;
padding: 0.25em 1em;
${props =>
props.primary &&
css`
background: palevioletred;
color: white;
`};
`
How you would use it in the react renderer:
render(
<Container>
<Button>Normal Button</Button>
<Button primary>Primary Button</Button>
</Container>
);

Why overflow-y is not working as expected

I'm trying to have a scrollbar on Y axis when text gets to the end of the right side of the div.
The problem is that when I have texts with spaces its working great but when I'm entering long text without spaces the text is overlapping to X axis.
This is how it's looks:
I'm working with styled components.
This is code samples:
// The container of the white box
const TodoContainer = styled.div`
display: flex;
`;
// The white box with text inside
const TextContainer = styled.div`
text-decoration: ${(props) => props.completed && "line-through"};
background: #fff;
color: ${(props) => props.theme.palette.text.primary};
font-weight: 500;
font-size: 1.6rem;
border-radius: 5px;
padding: 1rem;
margin-right: 1rem;
overflow-x: hidden;
overflow-y: auto;
min-width: 20rem;
max-width: 20rem;
height: 2rem;
`;
You would have to tell css to break long words into new line if needed.
Just add word-break: break-all.
const TextContainer = styled.div`
word-break: break-all;
...
`;
Note: You could also use overflow-wrap: break-word. Thanks #trixn

Overriding a styled-component with new styled-component?

I have a three styled-components StyledBanner and StyledTitle and WarningBanner.
I am looking to overwrite StyledBanner color through WarningBanner but is is not working out and the return Component always fall back to the div color.
How can I restyle a styled-component?
const StyledBanner = styled.div`
background-color: ${theme.colors.blue_2};
padding: 15px;
display: flex;
flex-direction: column;
border-radius: 4px;
width: 100%;
`;
const StyledTitle = styled(PG)`
font-size: 1.125rem;
font-weight: bold;
padding: 0px 0px 15px 0px;
colors: ${theme.colors.blue_2}
`
const WarningBanner = styled(StyledBanner)`
background-color: ${theme.colors.banner_yellow}
${StyledTitle}{
colors: ${theme.colors.textColor}
`
...
return (<WarningBanner> //still showing theme.colors.blue_2
{title ? <StyledTitle> //still showing theme.colors.blue_2
{title}
</StyledTitle> : null}
</WarningBanner>)
You have a minor typo in your posted code (the CSS property you want to set is color, not plural). Otherwise, your approach is perfectly valid.
Working example: https://codepen.io/mattlubner/pen/XWrvQLP
const theme = {
colors: {
red: '#f00',
greed: '#0f0',
blue: '#00f',
yellow: '#ff0',
},
};
const StyledBanner = window.styled.div`
background-color: ${theme.colors.blue};
padding: 15px;
display: flex;
flex-direction: column;
border-radius: 4px;
width: 100%;
`;
const StyledTitle = window.styled.h1`
font-size: 1.125rem;
font-weight: bold;
padding: 0px 0px 15px 0px;
color: ${theme.colors.blue};
`;
const WarningBanner = window.styled(StyledBanner)`
background-color: ${theme.colors.yellow};
${StyledTitle} {
color: ${theme.colors.green};
`;
ReactDOM.render(
<WarningBanner>
<StyledTitle>Example Title</StyledTitle>
</WarningBanner>,
document.getElementById('root'),
);

Resources