I have a unordered list like this,
return (
<div className="tailor-tabs">
<ul className="tailor-tab-list ">
{children.map((child) => {
const { label } = child.props;
return (
<TailorTab
activeTab={activeTab}
key={label}
label={label}
onClick={onClickTabItem}
/>
);
})}
</ul>
<div className="tailor-tab-content">
{children.map((child) => {
if (child.props.label !== activeTab) return undefined;
return child.props.children;
})}
</div>
</div>
);
And now I want to make that unordered list scrollable horizontally.
And I am making it horizontally scrollable like this,
.tailor-tab-list {
display: flex;
justify-content: center;
overflow-x: auto;
white-space: nowrap;
}
::-webkit-scrollbar {
display: none;
}
but the problem here is I cannot see the whole list, it shows most of the items but some items are hidden and can not be scrolled. like this,
Why is that? Any help?
After the list is made, it shows like this
Related
This question already has answers here:
Equal height rows in CSS Grid Layout
(2 answers)
Closed 4 days ago.
I have a group of list items that are essentially containers with some text content. On mobile screen dimensions, I want the height of the containers to be set automatically according to the text content. However, I want all of the containers to assume the same height. So in this case, each container height would equal the height of the tallest container. The codesandbox link is here.
As you can see in the screenshot below, on a 414 px screen, the third container in the list is taller than the other two. I want all the containers to assume the height of the third one so that they can be the same.
How can I accomplish this? Here is the relevant lines of code:
<div className="App">
<h1>Lender Benefits</h1>
<ul className="list">
{lenderBenefits.map((benefit) => (
<li className="benefit_container">{benefit}</li>
))}
</ul>
</div>
.list {
padding-left: 0;
width: 100%;
}
.benefit_container {
/* height: max-content; */
border-radius: 24px;
margin-bottom: 12px;
padding: 2px 8px;
font-size: 16px;
font-weight: 600;
background-color: #f1e8dc;
display: flex;
align-items: center;
}
The CSS Grid approach (preferred):
const styleForEqualHeightRows = {
display: "grid",
gridAutoRows: "1fr",
gap: "1rem" // <= Optional
}
export default function App() {
return (
<div className="App">
<h1>Lender Benefits</h1>
<ul
className="list"
style={styleForEqualHeightRows}
>
{lenderBenefits.map((benefit) => (
<li
key={benefit}
className="benefit_container"
>
{benefit}
</li>
))}
</ul>
</div>
);
}
Here's one (hacky) approach using JavaScript:
import { useLayoutEffect, useRef, useState } from "react";
import "./styles.css";
const lenderBenefits = [
"No repayment, just reduced margins through credit",
"A cash advance without predatory interest",
"Works with your community to bring them in on your mission"
];
export default function App() {
const list = useRef();
const [style, setStyle] = useState({});
useLayoutEffect(() => {
async function handleResize() {
await setStyle({}); // Break from batch updates...
const lis = list.current.querySelectorAll("li");
const heights = [...lis].map((li) => li.offsetHeight);
const maxHeight = Math.max(...heights);
setStyle({ height: maxHeight + "px" });
}
handleResize();
window.addEventListener("resize", handleResize);
return function cleanUp() {
window.removeEventListener("resize", handleResize);
};
}, []);
return (
<div className="App">
<h1>Lender Benefits</h1>
<ul ref={list} className="list">
{lenderBenefits.map((benefit) => (
<li key={benefit} style={style} className="benefit_container">
{benefit}
</li>
))}
</ul>
</div>
);
}
I have element with width 400% and I want to move it to left by using translateX(-(index/4)*100%) when focused index changes.
Changing focused element translateX property with tab keyboard button displays it wrong on middle elements (1,2) even though using same hardcoded styling works as expected. What am I missing here?
const {useState} = React;
const App = () => {
const [curr, setCurr] = useState(0);
const carouselStyles = {
transform: `translateX(${-(curr / 4) * 100}%)`
// uncomment to see that styling works fine with hardcoded values 1,2..
// transform: `translateX(${-(1 / 4) * 100}%)`
};
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
}
};
console.log(carouselStyles);
return (
<div>
<div className="carousel" style={carouselStyles}>
<div className="item">
11 very long text
<a href="/111" onFocus={() => handleFocus(0)}>
11111
</a>
</div>
<div className="item">
22 very long text
<a href="/222" onFocus={() => handleFocus(1)}>
22222
</a>
</div>
<div className="item">
33 very long text
<a href="/333" onFocus={() => handleFocus(2)}>
33333
</a>
</div>
<div className="item">
44 very long text
<a href="/444" onFocus={() => handleFocus(3)}>
44444
</a>
</div>
</div>
current: {curr}
</div>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
.carousel {
display: flex;
align-items: center;
width: 400%;
}
.item {
flex: 0 1 100%;
text-align: center;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I needed to prevent the scrolling and in my provided example its enough to add this line into handleFocus function
window.scrollTo(0, 0);
But in my real scenario parent wrapper also had overflow: hidden; which prevented above code from working. So I've used refs
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
carouselRef.current.scrollTo(0, 0);
}
};
return (
<div ref={carouselRef}>
<div className="carousel" style={carouselStyles}>
...
</div>
current: {curr}
</div>
);
I need to list out a long name list inside my page while showing all names at first is not desirable.
So I try to add an expand more button on it.
However, using a button will keep the browser focus on that button after it's pressed, left the button position unchanged on the screen while the name was inserted before that button.
On the other hand, using any, not focusable element (eg. div with onclick function) will do the desired behavior but lost the accessibility at all. Making the "button" only clickable but not focusable.
How do I make the button flushed to list bottom like the snippet div block does? Or is there a better choice to expand the existing list?
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Removing focus from the button in the click handler is probably the most elegant approach: e.target.blur(). It will work on any HTML element, whether it is focusable or not (as with the div in your case).
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
e.target.blur()
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Inspired by #MiKo, temporally unmount the button after click and set a timeout to add it back seems to do the work. Since browser lose the focus on original expand button, this will keep content flush down without focusing the original button:
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const [showBtn, setShowBtn] = React.useState(true)
const handleExpand = e => {
setShowBtn(false)
setIdx(idx + 1)
setTimeout(() => setShowBtn(true), 10)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
{showBtn?
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div> :
<div></div>
}
</div>
}
But I'm still looking a method that doesn't need to 'unmount' a thing which should be there all time.
in React i am attempting to add my api content to a series of tabs, so you click on a tab, and it will show any listings in the api matching that date.
my attempt, code of component is below
import context from "./apiContext";
import styled from "styled-components";
const Tab = styled.button`
font-size: 20px;
padding: 10px 60px;
cursor: pointer;
opacity: 0.6;
background: white;
border: 0;
outline: 0;
${({ active }) =>
active &&
`
border-bottom: 2px solid black;
opacity: 1;
`}
`;
const ButtonGroup = styled.div`
display: flex;
`;
const tabs = ["18-08-20", "19-08-20", "20-08-20"];
function Movies() {
const { films } = useContext(context);
console.log(films);
const [active, setActive] = useState(tabs[0]);
return (
<>
<div className="movies">
<div className="title">
<h1>
Movies: <span />
All Releases
</h1>
<div className="tab-menu">
<ButtonGroup>
{tabs.map((tab) => (
<Tab
key={tab.label}
active={active === tab}
onClick={() => setActive(tab)}
>
{tab.label}
</Tab>
))}
</ButtonGroup>
</div>
</div>
<div className="content">
{films
.filter((item) => item.PreShowStartTime === Date.parse({ active }))
.map((item, index) => (
<div class="card" key={index}>
<div class="title">
<span class="title">{item.Title}</span>{" "}
<span>
{new Date(item.PreShowStartTime).toLocaleDateString("en", {
day: "2-digit",
month: "short"
})}
</span>
</div>
</div>
))}
</div>
</div>
</>
);
}
export default Movies;
To view with live API:
https://codesandbox.io/s/strange-hoover-8hik2
On line 24, is my test array of dates, which also should show as the label of the tab.
You are going to have to format the dates returned by the API according to what you have defined in the tabs constant in your code (or vice versa). Currently your API returns example 2020-08-18T14:00:00 but your tabs has format 18-08-20.
So for that, on your filter condition, you can use something like:
new Date(item.PreShowStartTime).toJSON().slice(2, 10) === active
where active is the active date selected on the tab
https://codesandbox.io/s/reverent-yonath-pv8i0?file=/src/Movies.js:1293-1357
As a side note, you were also accessing tab.label to print your tab labels. tabs does not contain a property named label...
I need the user's initials to appear on the avatars (the one in the menu is an SVG), just a grey circle with those initials. I have the function but now I have no idea how to call it in the JSX of the Dropdown menu (which is a SemanticUI library). Any cues?
const textToImage = require('text-to-image')
componentWillMount() {
let P = "", N = ""
if (res.data.Item.firstName && res.data.Item.firstName.length > 0) P = res.data.Item.firstName.charAt(0).toUpperCase()
if (res.data.Item.lastName && res.data.Item.lastName.length > 0) N = res.data.Item.lastName.charAt(0).toUpperCase()
this.setState({
initials: P + N
}, () => {
textToImage.generate(this.state.initials, { maxWidth: 30, maxHeight: 30 })
.then(dataUri => {
this.setState({ avatarInitiales: dataUri })
})
});
})
}
render() {
let avatarImage;
let userInitials;
let nomComplet;
if (this.state.user) {
avatarImage =
this.state.user.avatarImage === null || this.state.user.avatarImage === "image.jpg"
? (!this.props.pochette ?
"data:image/png;base64," + biquetteBase64
: "https://images-publiques.s3.amazonaws.com/avatar.png")
: `https://smartsplit-images.s3.us-east-2.amazonaws.com/${this.state.user.avatarImage}`;
userInitials =
this.state.user.avatarImage === null ? this.state.initials : null;
nomComplet = this.state.user.artistName
? this.state.user.artistName
: `${this.state.user.firstName} ${this.state.user.lastName}`;
}
let menu = (
<Dropdown text="" icon="angle down big black">
<Dropdown.Menu icon="down small">
<Dropdown.Item
content={nomComplet}
text={this.state.initials} //Not sure what to do around here
image={<AvatarInitialsSVG />}
/>
</Dropdown.Menu>
</Dropdown>
);
return (
<>
<div className="ui five wide column avatar--image profile"></div>
{nomComplet}
</Label>
//And here
{!userInitials && (
<img src={avatarImage} alt="user--avatar" className="user--img" />
)}
{menu}
</>
);
}
You probably need to add custom HTML + CSS, and re-use some of the classes from Semantic-UI.
Try something like this:
<Dropdown.Item>
<React.Fragment>
<div className="custom-initials-holder">
<AvatarInitialsSVG/>
<span className="custom-initials">{this.state.initials}</span>
</div>
<span className="text">{nomComplet}</span>
</React.Fragment>
</Dropdown.Item>
And your custom CSS overwrites:
.custom-initials-holder {
display: inline-block;
width: 24px;
height: 24px;
margin-right: 12px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
}
.custom-initials-holder > svg {
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
Tweak according to your needs.
Small demo