Styled component css helper not working with mixin - styled-components

I want to change the StyledButton depends on the props passed. When complex is true, it's supposed to go to complexMixin and then determine what whiteColor passes. But even I pass whiteColor with true, the color is black, not white.
Does anyone have idea what's going on with it? Thanks!
Demo
import styled, { css } from "styled-components";
const complexMixin = css`
color: ${({ whiteColor }) => (whiteColor ? "white" : "black")};
`;
const StyledButton = styled.div`
border: 1px solid red;
height: 100px;
text-align: center;
margin: 20px;
font-weight: 700;
font-size: 40px;
color: ${({ complex }) => (complex ? complexMixin : "green")};
`;
const App = () => {
const isComplex = true;
const isWhite = true;
return (
<>
<StyledButton whiteColor={isWhite} complex={isComplex}>
ccc
</StyledButton>
<StyledButton complex={!isComplex}>BBB</StyledButton>
</>
);
};

usually, I write this kind of complicated CSS logic like this.
const complexMixin = css`
${({ complex, whiteColor }) =>
complex &&
css`
color: #000;
${whiteColor &&
css`
color: #fff;
`}
`}
${({ complex }) =>
!complex &&
css`
color: green;
`}
`;
const StyledButton = styled.div`
border: 1px solid red;
height: 100px;
text-align: center;
margin: 20px;
font-weight: 700;
font-size: 40px;
${complexMixin}
`;

For your mixin you can do this:
const complexMixin = ({ whiteColor }) => whiteColor ? "white" : "black";
And
color: ${({ complex }) => (complex ? complexMixin : "green")};
In your code we see that you want to add a "color: " in another "color: "

Related

Size of the map when redenring mapbox in React

I trying to use mapbox GL to render a map in React.
I've diveide my screen in 4(2 col/crow) with Grid with the following css:
.container {
font-family: sans-serif;
margin: 0;
display: grid;
grid-template-rows: 1fr 7fr;
grid-template-columns: auto;
gap: 5px;
grid-template-areas: "cr1 cr2"
"cr1 cr4";
height: 100vh;
width: 200vh;
background-color: chartreuse;
}
.table {
grid-area: cr1 ;
background-color: aqua;
display: flex;
justify-content: center;
align-items: center;
}
.tank-select {
grid-area: cr2;
background-color: grey;
}
.map {
grid-area: cr4;
display: flex;
justify-content:space-around;
align-items:stretch;
background-color: blueviolet;
}
i would like to have the map in the box cr4 with the following code:
<div className="container">
<div className="table">test</div>
<div className="tank-select">test2</div>
<div className="map">
<Map />
</div>
</div>
The map is compute this way:
const mapContainerRef = useRef(null)
const [lng, setLng] = useState(5)
const [lat, setLat] = useState(34)
const [zoom, setZoom] = useState(1.5)
// Initialize map when component mounts
useEffect(() => {
const map = new mapboxgl.Map({
container: mapContainerRef.current,
style: 'mapbox://styles/mapbox/streets-v11',
center: [lng, lat],
zoom: zoom,
})
// Add navigation control (the +/- zoom buttons)
map.addControl(new mapboxgl.NavigationControl(), 'top-right')
map.on('move', () => {
setLng(map.getCenter().lng.toFixed(4))
setLat(map.getCenter().lat.toFixed(4))
setZoom(map.getZoom().toFixed(2))
})
// const marker = new mapboxgl.Marker().setLngLat(TankLocationArray).addTo(map)
//
map.on('load', function () {
map.addSource('route', {
type: 'geojson',
data: '../../assets/data/test.geoJSON',
})
map.addLayer({
id: 'route',
type: 'symbol',
source: 'route',
paint: {
'circle-radius': 10,
'circle-color': '#ff0000',
},
})
})
//
// Clean up on unmount
return () => map.remove()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
return (
<div>
<div className="sidebarStyle">
<div>
Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
</div>
</div>
<div className="map-container" ref={mapContainerRef} />
</div>
With the css:
.map-container {
position:relative;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.sidebarStyle {
display: inline-block;
position: absolute;
top: 0;
left: 0;
margin: 12px;
background-color: #404040;
color: #ffffff;
z-index: 1 !important;
padding: 6px;
font-weight: bold;
}
When i change the map-container position from relative to absolute, the map would take the whole screen, otherwise it's not visible.
I do not know how to style to get the map to appear only in the box .map(cr4).
thanks for the help
solved the issue using React Map GL library.
works really fine. https://www.npmjs.com/package/react-map-gl

How to list Items in rows of 3 in React

I have a list of items that I want to display in rows of 3 but it's not displaying in 3 rows. it works in the normal HTML but doesn't work in React.
Here is my Event Component
const Event = (props) => (
<div className="wrap">
<div className="tile">
<img src={props.event.eventImage} />
<div className="text">
<h1>{props.event.title}</h1>
<h2 className="animate-text">More lorem ipsum bacon ipsum.</h2>
<p className="animate-text">{props.event.description}</p>
<div className="dots">
<span />
<span />
<span />
</div>
</div>
</div>
</div>
);
export default class EventsList extends Component {
constructor(props) {
super(props);
this.state = { events: [] };
}
componentDidMount() {
axios
.get("http://localhost:9000/events/")
.then((response) => {
this.setState({ events: response.data });
})
.catch(function (error) {
console.log(error);
});
}
eventList() {
return this.state.events.map(function (currentEvent, i) {
return <Event event={currentEvent} key={i} />;
});
}
render() {
return (
<div>
<div className="row">{this.eventList()}</div>
</div>
);
}
}
and my css
.wrap {
margin: 50px auto 0 auto;
width: 100%;
display: flex;
align-items: space-around;
max-width: 1200px;
}
.tile {
width: 380px;
height: 380px;
margin: 10px;
background-color: #99aeff;
display: inline-block;
background-size: cover;
position: relative;
cursor: pointer;
transition: all 0.4s ease-out;
box-shadow: 0px 35px 77px -17px rgba(0, 0, 0, 0.44);
overflow: hidden;
color: white;
font-family: "Roboto";
}
.tile img {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 0;
transition: all 0.4s ease-out;
}
.tile .text {
/* z-index:99; */
position: absolute;
padding: 30px;
height: calc(100% - 60px);
}
.tile h1 {
font-weight: 300;
margin: 0;
text-shadow: 2px 2px 10px rgba(0, 0, 0, 0.3);
}
.tile h2 {
font-weight: 100;
margin: 20px 0 0 0;
font-style: italic;
transform: translateX(200px);
}
.tile p {
font-weight: 300;
margin: 20px 0 0 0;
line-height: 25px;
/* opacity:0; */
transform: translateX(-200px);
transition-delay: 0.2s;
}
.animate-text {
opacity: 0;
transition: all 0.6s ease-in-out;
}
.tile:hover {
/* background-color:#99aeff; */
box-shadow: 0px 35px 77px -17px rgba(0, 0, 0, 0.64);
transform: scale(1.05);
}
.tile:hover img {
opacity: 0.2;
}
.tile:hover .animate-text {
transform: translateX(0);
opacity: 1;
}
.dots {
position: absolute;
bottom: 20px;
right: 30px;
margin: 0 auto;
width: 30px;
height: 30px;
color: currentColor;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
}
.dots span {
width: 5px;
height: 5px;
background-color: currentColor;
border-radius: 50%;
display: block;
opacity: 0;
transition: transform 0.4s ease-out, opacity 0.5s ease;
transform: translateY(30px);
}
.tile:hover span {
opacity: 1;
transform: translateY(0px);
}
.dots span:nth-child(1) {
transition-delay: 0.05s;
}
.dots span:nth-child(2) {
transition-delay: 0.1s;
}
.dots span:nth-child(3) {
transition-delay: 0.15s;
}
#media (max-width: 1000px) {
.wrap {
flex-direction: column;
width: 400px;
}
}
This is how it's displaying.
But this is what I want to achieve.
I am new to React and I'm learning by building a project.
Here is codepen link of what I want to achieve.
Codepen Link
I have a feeling it's your css, you have your wrap class stretching the whole row width and within it, there is a tile that's only 380px.
Consider making row a grid with three columns.
Ciao, you can use display: 'in-line' like this:
return <Event event={currentEvent} key={i} style={{display: 'in-line'}}/>;

I want to use the url I got as props from the styled component

import React from "react";
import styled from "styled-components";
const topCate = (props) => {
return (
<TopCateList imgObj>
<p>{props.main_category_id}</p>
</TopCateList>
);
}
const TopCateList = styled.li`
width: 130px;
height: 60px;
border-radius: .3em;
border: 1px solid gray;
padding: .5em 0em;
text-align: center;
font-size: 0.975rem;
background-image: url(${props => props.backImg});
line-height: 3em;
font-weight: 500;
margin-right: .5em;
`;
export default topCate;
I want to use the url I got as props from the styled component.
It is not displayed on the screen.
What should I do?
You can extend a component in styled-components like in the code below :
Any props you pass in this component will be available to you as props like the url,color and backImg. Just make sure to pass the className to the li for it to apply the styles.
import React from "react";
import styled from "styled-components";
const List = (props) => {
return (
<li className={props.className}>
<p>{props.url}</p>
</li>
);
}
const TopCateList = styled(List)`
width: 130px;
height: 60px;
border-radius: .3em;
border: 1px solid gray;
padding: .5em 0em;
text-align: center;
font-size: 0.975rem;
background-image: url(${props => props.backImg});
line-height: 3em;
font-weight: 500;
margin-right: .5em;
color: ${props => props.color};
`;
export default TopCateList;
Usage
<TopCateList url="http://stackoverflow.com" color="red" backImg="./src/someimage.jpg">
Hope this helps !

How to conditionally animate with styled-components

I'm trying to animate a line when an input is checked with styled-components.
I've tried with (a) and without (b) keyframes, and both return (c):
(a)
import styled, { keyframes } from 'styled-components';
const Input = styled.input``
const NavIconLine = styled.span`
display: block;
position: absolute;
height: 3px;
`;
const Animation = keyframes`
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
width: 70%;
}
to {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
width: 100%;
}
`;
const NavIconLine2 = styled(NavIconLine)`
width: 70%;
${Input}:checked ~ & {
animation: ${Animation} 0.15s ease-in-out;
}
`;
(b)
import styled from 'styled-components';
const Input = styled.input``
const NavIconLine = styled.span`
display: block;
position: absolute;
height: 3px;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transition: 0.15s ease-in-out;
transition: 0.15s ease-in-out;
`;
const NavIconLine2 = styled(NavIconLine)`
width: 70%;
${Input}:checked ~ & {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
width: 100%;
}
`;
(c)
function Component {
return (
<Input type="checkbox" id="menu" />
<NavIcon for="menu">
<NavIconLine1 />
</NavIcon>
)
}
I think that perhaps the issue is with the targeting of the element in "${Input}:checked ~ & {...}" as "~ &" refers to the sibling and not the parent's sibling? If that's the case, is possible to target it?
Thanks!
Solved:
Instad of writing the condition on the descendant, it's written on the parent's sibling:
const Input = styled.input`
display: whatever;
:checked + ${NavIcon} {
${NavIconLine1} {
animation: whatever;
}
}
`;

ReactJS responsive carousel from scratch

Okay, so basically I am trying to write a carousel for ReactJS from scratch.
I've accomplished to code must of the features required as navigation, slides and so on.
The main purpose of this is to make it responsive, but that is what I am having a hard time figuring out how to do.
carousel.js
import React from 'react'
import { NavLink } from 'react-router-dom'
import classNames from 'classnames'
export default class Carousel extends React.Component {
constructor(props) {
super(props)
this.handleNext = this.handleNext.bind(this)
this.handlePrev = this.handlePrev.bind(this)
this.state = {
innerWidth: 0,
navPrevDisabled: true
}
}
componentWillMount() {
var stateItems = []
for(let i in this.props.items) {
stateItems.push(
<div className="react-item movie" key={i}>
<div className="front">
<div className="front-image" style={{backgroundImage: `url(${this.props.items[i].poster})`}}>
</div>
<div className="backdrop medium">
<div className="react-play-button fill">
<figure className="icon-content"></figure>
</div>
</div>
</div>
</div>
)
}
this.setState({
items: stateItems
})
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateCarousel, false)
}
updateCarousel = () => {
var $reactItem = $('.carousel-wrapper .carousel-inner .react-item')
var maxw = $('.carousel-wrapper').width()
/*var slideItemsFit = $reactItem.filter(function () {
return $(this).position().left < maxw
}).length*/
var itemsFitSlide = Math.floor(maxw / $reactItem.outerWidth(true))
console.log(itemsFitSlide)
var margin = $reactItem.outerWidth(true) * itemsFitSlide
margin = (maxw - margin) / itemsFitSlide / 2
$reactItem.css({marginLeft: `${margin}px`, marginRight: `${margin}px`})
this.setState({
itemsShownTotal: itemsFitSlide,
itemWidth: $reactItem.outerWidth(true),
navNextDisabled: (itemsFitSlide === this.state.items.length),
itemsFitSlide: itemsFitSlide
})
}
componentDidMount() {
this.updateCarousel()
window.addEventListener("resize", this.updateCarousel, false)
}
handleNext(e) {
var state = this.state
var maxw = $('.carousel-wrapper').width()
var innerWidth = -Math.abs(state.innerWidth - (state.itemWidth * state.itemsFitSlide))
var itemsLeft = state.items.length - state.itemsShownTotal
var lastSlide = state.itemsFitSlide > itemsLeft
if(lastSlide) {
innerWidth = -Math.abs(state.innerWidth - (state.itemWidth * itemsLeft))
}
var itemsShownTotal = (lastSlide)
? state.itemsShownTotal + itemsLeft
: state.itemsShownTotal + state.itemsFitSlide
this.setState({
itemsShownTotal: itemsShownTotal,
innerWidth: innerWidth,
navPrevDisabled: false,
navNextDisabled: itemsShownTotal === state.items.length
})
}
handlePrev(e) {
var state = this.state
var innerWidth = state.innerWidth + (state.itemWidth * state.itemsFitSlide)
//var itemsLeft = state.itemsFitSlide - state.itemsShownTotal
var firstSlide = false
if(innerWidth >= 0) {
firstSlide = true
innerWidth = 0
}
var itemsShownTotal = (firstSlide)
? state.itemsFitSlide
: state.itemsShownTotal - state.itemsFitSlide
this.setState({
itemsShownTotal: itemsShownTotal,
innerWidth: innerWidth,
navPrevDisabled: firstSlide,
navNextDisabled: false
})
}
render() {
var nav = {
prev: classNames({
navigation: true,
prev: true,
disabled: this.state.navPrevDisabled,
whiteframe: true
}),
next: classNames({
navigation: true,
next: true,
disabled: this.state.navNextDisabled,
whiteframe: true
})
}
return (
<section className="block collection carousel portrait">
<div className="scaffold">
<i className={nav.prev} onClick={this.handlePrev}></i>
<i className={nav.next} onClick={this.handleNext}></i>
<header className="collection-header">
<NavLink to={this.props.route}>
<h2>{this.props.title}</h2>
</NavLink>
</header>
<div className="carousel-wrapper">
<div className="carousel-inner use-transition" style={{transform: `translateX(${this.state.innerWidth}px)`}}>
{this.state.items}
</div>
</div>
</div>
</section>
)
}
}
Basically let's say there's 5 images in a slide, but only width enough in the wrapper to fit 4 images, I then want to redo the process and margin the 4 images to fit the center of the wrapper.
carousel.css
.scaffold {
position: relative;
}
.block.collection .collection-header {
margin: 0 0 20px;
position: relative;
padding-top: 20px;
}
.block.collection .collection-header h2 {
outline: none;
text-decoration: none;
margin: 0;
color: #212d33;
font-size: 2.6rem;
letter-spacing: -0.01em;
line-height: 36px;
font-weight: normal;
}
.block.collection.carousel .carousel-wrapper {
overflow: hidden;
width: 100%;
}
.block.collection.carousel .carousel-wrapper .carousel-inner {
display: inline-block;
margin-right: 5px;
white-space: nowrap;
}
.block.collection.carousel .carousel-wrapper .carousel-inner.use-transition {
transition: transform 0.5s ease-out;
}
.block.collection.portrait .react-item {
width: 186px;
margin: 0 0px 20px 0px;
}
.block.collection.portrait .react-item.hidden {
opacity: 0;
}
.block.collection .react-item {
position: relative;
display: inline-block;
vertical-align: top;
margin-top: 10px;
margin-bottom: 10px;
}
.block.collection.portrait .react-item .front {
height: 279px;
}
.block.collection .react-item .front {
background-color: #212d33;
position: relative;
display: block;
text-decoration: none;
}
.block.collection .react-item .front .front-image {
animation: fadein 500ms;
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
background-size: cover;
}
.block.collection .react-item .backdrop {
transition: opacity 0.1s ease-in-out;
opacity: 0;
height: 100%;
position: absolute;
width: 100%;
top: 0;
left: 0;
z-index: 5;
text-align: center;
background: rgba(15, 22, 26, 0.25);
}
.block.collection .react-item .backdrop:hover {
opacity: 1;
}
.react-play-button.fill {
margin: 0;
width: 100%;
height: 100%;
background-size: 68px;
}
.react-play-button {
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
border: 0;
padding: 0;
cursor: pointer;
}
.react-play-button .icon-content {
width: 68px;
height: 68px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyBoZWlnaHQ9IjEwMCUiIHdpZHRoPSIxMDAlIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgdmVyc2lvbj0iMS4wIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KICAgIDxzdHlsZT48IVtDREFUQVsKICAgICAgICBnIHsKICAgICAgICAgICAgc3Ryb2tlOiAjZmZmOwogICAgICAgIH0KICAgICAgICBnIGNpcmNsZSB7CiAgICAgICAgICAgIGZpbGw6IHJnYmEoMCwwLDAsMC4wKTsKICAgICAgICAgICAgc3Ryb2tlLXdpZHRoOiA0OwogICAgICAgIH0KICAgICAgICBnIHBvbHlsaW5lIHsKICAgICAgICAgICAgZmlsbDogdHJhbnNwYXJlbnQ7CiAgICAgICAgICAgIHN0cm9rZS13aWR0aDogMTA7CiAgICAgICAgfQogICAgXV0+PC9zdHlsZT4KCiAgICA8Zz4KICAgICAgICA8Y2lyY2xlIHI9IjQ4IiBjeD0iNTAiIGN5PSI1MCIgLz4KICAgICAgICA8cG9seWxpbmUgcG9pbnRzPSI0MSwyOCA2Myw1MCA0MSw3MiIgLz4KICAgIDwvZz4KPC9zdmc+);
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.block.collection.carousel .navigation {
top: 49%;
border: none;
cursor: pointer;
position: absolute;
width: 66px;
background: rgba(249, 249, 251, 0.7);
width: 48px;
height: 60px;
display: block;
font-size: 0;
border-radius: 2px;
z-index: 10;
opacity: 1;
pointer-events: auto;
transition: opacity .3s ease-in-out;
}
.block.collection.carousel .navigation.disabled {
opacity: 0;
pointer-events: none;
}
.block.collection.carousel .navigation:hover {
background-color: #FFF;
}
.block.collection.carousel .navigation:hover::after {
opacity: 1;
}
.block.collection.carousel .navigation.prev {
left: -25px;
}
.block.collection.carousel .navigation.next {
right: -25px;
}
.block.collection.carousel .navigation::after {
transition: opacity .5s ease;
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMy40IDI0IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMy40IDI0OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzIxMkQzMzt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0wLDEuNEwxMC42LDEyTDAsMjIuNkwxLjQsMjRsMTItMTJMMS40LDBMMCwxLjR6Ii8+Cjwvc3ZnPgo=) no-repeat;
width: 16px;
height: 24px;
content: '';
position: absolute;
top: 18px;
left: 18px;
opacity: 0.7;
}
.block.collection.carousel .navigation.prev::after {
left: 13px;
transform: rotate(180deg);
}

Resources