Hello everyone, I could really use some assistance here. The problem I'm facing involves a flex-container with tabs (the number of tabs can vary as they are passed as an array in a React component).
Each tab consists of a label and a span with a number. According to the initial conditions for this task, the label should not be shorter than 3 letters + "..."
(we use ellipsis to indicate this).
From what I understand, the only solution is to manually code this (as the ch unit is based on the '0' symbol, leading to inaccuracies). But now, onto the main issue. The text in the label can vary in length, and we can have a different number of tabs.
I need to arrange the tabs in the container (which is restricted by a max-width
of 900px
) as efficiently as possible. What this means is that we set the tabs with the full length of the label if it's possible, if not - the label shrinks until it reaches the min-width (6ch)
. If the number of tabs is too large (where all labels have reached min-width but the tabs exceed the container), I won't display them at all. I plan to implement this using useLayoutEffect
with checks for exceeding the container.
The main problem currently is that spans overflow the tabs, meaning that labels have the ability to shrink, but instead, other tabs start shrinking and this causes issues with the span. I've tried using a grid with template columns of 1fr
width (where the number of columns can be set by passing the array length to a styled component). While this works, I need the tabs to be aligned to the left side (instead of taking up all available space) and I'm experiencing problems with extra empty space if label + gap + span < 1fr
of the container.
At this moment, I don't have a solution other than hardcoding the min-width of the tab, but we all understand that this is unacceptable (especially considering there could be 10,000, for example, in the span). I humbly request assistance. Finding a solution would make me the happiest person.
I have included images demonstrating the issue, code snippets, and a link to the CodeSandbox example (where you can insert tabs in the mock_data and change the word lengths). CodeSandBox - https://codesandbox.io/s/gracious-dijkstra-61s9sp?file=/src/Component.jsx:0-1606
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 decision: hardcode min-width
// of tab ~ 88px (53px for the first - it will always be "all")