this is a react app. my css class is not changing even though the state assign to track the boolean value is changing. I've passed my states accordingly for this still not working. here is the github url
my app.js file -
import React, { useState } from "react";
// Import Styles
import "./styles/App.css";
// Import Components
import Hello from "./components/Hello";
import Rectangle from "./components/Rectangle";
import Button from "./components/Button";
function App() {
const [colorStatus, setColorStatus] = useState(false);
return (
<div className="App">
<Hello />
<Rectangle colorStatus={colorStatus} />
<Button colorStatus={colorStatus} setColorStatus={setColorStatus} />
</div>
);
}
export default App;
button.js -\
import React from "react";
const Button = ({ colorStatus, setColorStatus }) => {
return (
<div className="button">
<button className="btn-1" onClick={() => setColorStatus(!colorStatus)}>
Press
</button>
</div>
);
};
export default Button;
rectangle.js -
import React from "react";
const Rectangle = ({colorStatus}) => {
return <div className={`rectangle ${colorStatus ? 'active-rectangle' : '' }`}></div>;
};
export default Rectangle;
necessary css -
.rectangle {
width: 50vw;
height: 50vh;
background: rgba(255, 0, 0, 0.4);
position: fixed;
top: 30%;
left: 35%;
}
.active-rectangle {
width: 50vw;
height: 50vh;
background: rgba(0, green, 0, 0.4);
position: fixed;
top: 30%;
left: 35%;
}
Your .active-rectangle css-rule needs correction like so :-
.active-rectangle {
width: 50vw;
height: 50vh;
background: rgba(0, 255, 0, 0.4);
position: fixed;
top: 30%;
left: 35%;
}
So your state should be getting updated and the active-rectangle class is getting applied. Just the background property isn't getting applied properly. Rest must still be getting applied.
background isn't getting applied properly because you cannot use green when a decimal value is expected inside rgba(...).
Actually your css class is getting changed (according to the url you provided).
Problem seems to be with the usage of background: rgba(0, green, 0, 0.4). Specifically in rgba(0, green, 0, 0.4).
Instead of green, it should have been a number from 0 to 255 to denote the green part of the colour.
G of R-G-B-A from rgba function you are using
checkout https://cssreference.io/property/background-color/
The problem, as said here before is the background property
Lets refactor the code a bit while we are in it.
.rectangle {
width: 50vw;
height: 50vh;
background: rgba(255, 0, 0, 0.4);
position: fixed;
top: 30%;
left: 35%;
}
.rectangle.active {
background: rgba(0, 255, 0, 0.4);
}
import React from "react";
const Rectangle = ({colorStatus}) => {
return <div className={`rectangle ${colorStatus ? 'active' : '' }`}></div>;
};
export default Rectangle;
Sandbox: https://codesandbox.io/s/friendly-galileo-gwzbh?file=/src/components/Rectangle.js
Related
I'm converting elements over from jQuery into Vue 3 JS.
I'm creating a component called "CardID" that will have person's initials.
In some cases it will be selectable, in others not. So, I'm passing that class when I'm using the component to determine if it is selectable or not.
If a user clicks on it and it has the class "is-selectable", a blue border will go around it. Clicks again, and it goes away.
In Vue3, I believe the only way I can get the className on the element is through using the event.target.
This works great if I click the grey area of the button. But if I click on the white circle or the letter, it won't work.
Any help will be appreciated. I know the same thing happens in vanilla JavaScript.
Using the component...
<CardID class="is-selectable" symbolText="K" />
The code:
<template>
<button class="card" :class="{active: isActive}" #click="checkClass">
<div class="card__circle">
<div class="card__symbol">{{ symbolText }}</div>
</div>
</button>
</template>
<script>
export default {
name: "CardID",
data: function () {
return {
isActive: false
}
},
props: {
symbolText: String,
},
methods: {
checkClass(evt) {
if(evt.target.className.includes("is-selectable")) {
this.isActive = !this.isActive;
}
}
}
}
</script>
<style scoped>
.active {
border: 2px solid #1971D4;
box-shadow: inset 0px 0px 0px 4px #fff;
}
.card {
position: relative;
width: 160px;
height: 115px;
border-radius: 8px;
border: 2px solid transparent;
background-color: #E7E7E8;
}
.card__circle {
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
width: 60px;
height: 60px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.7);
transform: translate(-50%, -50%);
}
.card__symbol {
position: absolute;
top: 50%;
left: 50%;
z-index: 20;
font-size: 2.25rem;
color: #1d242b;
transform: translate(-50%, -50%);
}
Custom components can have custom props, so you should create a selectable prop in you CardID component and use that to check behaviour.
Something like:
<template>
<button class="card" :class="{active: isActive}" #click="checkClass">
<div class="card__circle">
<div class="card__symbol">{{ symbolText }}</div>
</div>
</button>
</template>
<script>
export default {
name: "CardID",
data: function () {
return {
isActive: false
}
},
props: {
selectable: Boolean,
symbolText: String,
},
methods: {
checkClass(evt) {
if(this.selectable) {
this.isActive = !this.isActive;
}
}
}
}
</script>
And use your component like:
// non selectable card
<CardID :selectable="false" symbolText="K" />
// selectable card
<CardID :selectable="true" symbolText="K" />
PS: also note that you can set a default value for your props, which is usually useful for boolean properties
Try to use this.$el.className instead of evt.target.className :
<template>
<button class="card" :class="{active: isActive}" #click="checkClass">
<div class="card__circle">
<div class="card__symbol">{{ symbolText }}</div>
</div>
</button>
</template>
<script>
export default {
name: "CardID",
data: function () {
return {
isActive: false
}
},
props: {
symbolText: String,
},
methods: {
checkClass(evt) {
if(this.$el.className.includes("is-selectable")) {
this.isActive = !this.isActive;
}
}
}
}
</script>
I am facing a weird CSS issue in my React project. A particular part of the JSX <div> has a class applied to it and added some style properties in the main .css file of the project. In local development, everything works fine but as soon as the build is created and uploaded to the production server, that particular part of the JSX <div> CSS class changes and the styling gets distorted.
Example:
Original JSX
import React, { useEffect, useState, useContext } from "react";
import Tooltip from "#material-ui/core/Tooltip";
import { withStyles, makeStyles } from "#material-ui/core/styles";
import Slider from "#material-ui/core/Slider";
const useStyles = makeStyles((theme) => ({
root: {
width: 450,
},
margin: {
height: 100,
},
}));
const PrettoSlider = withStyles({
root: {
color: "red",
height: 8,
},
thumb: {
height: 24,
width: 24,
backgroundColor: "#fff",
border: "2px solid currentColor",
marginTop: -8,
marginLeft: -12,
"&:focus,&:hover,&$active": {
boxShadow: "inherit",
border: "2px solid #fff407 !important",
},
},
active: {
backgroundColor: "#fff407",
},
})(Slider);
const CustomizedSlider = ({
id,
abbr,
type,
minElig,
maxElig,
}) => {
useEffect(() => {
setValue(sliderPreviousValue);
}, [sliderPreviousValue]);
const classes = useStyles();
return (
<>
<div className={classes.root}>
{type === "intervention" ? (
<ProgressBar max={maxElig} value={sliderValue} />
) : null}
{renderSlider}
</div>
</>
);
};
Original DOM:
<div class="diabMetr clearfix">
<span class="diabLabl">Diabetes</span>
<div class="makeStyles-root-1">
<span class="MuiSlider-root WithStyles(ForwardRef(Slider))-root-3 MuiSlider-colorPrimary"><span class="MuiSlider-rail WithStyles(ForwardRef(Slider))-rail-8"></span><span class="MuiSlider-track WithStyles(ForwardRef(Slider))-track-7" style="left: 0%; width: 83.3333%;"></span><input type="hidden" value="200"><span class="MuiSlider-thumb WithStyles(ForwardRef(Slider))-thumb-4 MuiSlider-thumbColorPrimary PrivateValueLabel-open-12 PrivateValueLabel-thumb-11" tabindex="0" role="slider" data-index="0" aria-label="pretto slider" aria-orientation="horizontal" aria-valuemax="240" aria-valuemin="0" aria-valuenow="200" style="left: 83.3333%;"><span class="PrivateValueLabel-offset-13 MuiSlider-valueLabel WithStyles(ForwardRef(Slider))-valueLabel-6"><span class="PrivateValueLabel-circle-14"><span class="PrivateValueLabel-label-15">200</span></span></span></span></span>
<div class="valueOuter clearfix"><label class="valueLeft">0</label><label class="valueRight">240</label></div>
</div>
</div>
The CSS for this JSX is:
.diabMetr {
padding-top: 10px;
span.diabLabl {
display: inline-block;
width: 200px;
text-align: left;
font-size: 12px;
line-height: 30px;
text-align: right;
#include respond-to(media-xl) {
width: 120px;
}
}
span.MuiSlider-root {
width: 100%;
padding: 0;
height: 0px;
.MuiSlider-rail {
height: 30px;
border-radius: 15px;
background: #e8e8e8;
opacity: 1;
}
.MuiSlider-track {
height: 30px;
background: #88d479;
border-radius: 15px;
}
.MuiSlider-thumb {
z-index: 12;
width: 35px;
height: 35px;
border-radius: 50%;
margin-left: -17px;
border: #88d479 solid 2px;
margin-top: -3px;
}
.MuiSlider-markLabel.MuiSlider-markLabelActive:last-child() {
right: 0 !important;
}
}
}
.makeStyles-root-1 {
width: calc(100% - 220px) !important;
float: right;
margin-top: -22px;
}
The DOM changes after build and uploaded to the server:
<div class="diabMetr clearfix">
<span class="diabLabl">Diabetes</span>
<div class="jss16">
<span class="MuiSlider-root jss18 MuiSlider-colorPrimary"><span class="MuiSlider-rail jss23"></span><span class="MuiSlider-track jss22" style="left: 0%; width: 83.3333%;"></span><input type="hidden" value="200"><span class="MuiSlider-thumb jss19 MuiSlider-thumbColorPrimary jss27 jss26" tabindex="0" role="slider" data-index="0" aria-label="pretto slider" aria-orientation="horizontal" aria-valuemax="240" aria-valuemin="0" aria-valuenow="200" style="left: 83.3333%;"><span class="jss28 MuiSlider-valueLabel jss21"><span class="jss29"><span class="jss30">200</span></span></span></span></span>
<div class="valueOuter clearfix"><label class="valueLeft">0</label><label class="valueRight">240</label></div>
</div>
</div>
The CSS for the class .jss16 is:
.jss16 {
width: 450px;
}
Issue to notice
Only the class .makeStyles-root-1 gets replaced with some random class .jss16 when the build gets uploaded to the server and the CSS changes accordingly, the rest of the JSX remains unchanged. I tried searching for the class .jss16 everywhere in the code, but it's not found. Also, everything works fine on localhost.
I tried adding the CSS properties to the .jss16 like this:
.jss16 {
width: 450px;
width: calc(100% - 220px) !important;
margin-top: -22px;
float: right;
}
and then re-initiate the uploading process but then instead of .jss16, another class is replaced something like .jss42. This keeps on repeating and does not work on any new build created.
I also tried the following CSS:
.diabMetr + span + div {
width: 450px;
width: calc(100% - 220px) !important;
margin-top: -22px;
float: right;
},
but this also didn't help. The styling of the app still remains distorted (incorrect, not as on localhost).
I spent several hours searching for this but in vain. If anyone can assist me in understanding this error and resolve the same, will be highly appreciated. Thanks in advance!
there are quite a few issues with this code. First in jsx CSS class is given as className as #Max has mentioned in his/her answer.
Another issue is that #material-ui's makeStyle doesn't work in this way. The classNames inside the makeStyle change to random names in the build stage. This happens to keep the classNames uniques, this is #material-ui's feature. I'd suggest you to read this #matrial-ui's documentation about makeStyles. And here a code example is provided.
To use makeStyles classes you've to hook it into your component.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
root: {
backgroundColor: 'red',
color: props => props.color,
},
});
export default function MyComponent(props) {
const classes = useStyles(props);
return (
<div className={classes.root}>
Lorem iosum poder
</div>
);
}
Update
According to your jsx code, add the styles which you've added in css class .makeStyles-root-1 in the useStyles object. It'll add the styles to the element.
After adding those CSS styles in useStyles this object will look like this:-
const useStyles = makeStyles((theme) => ({
root: {
width: 'calc(100% - 220px) !important',
float: 'right',
marginTop: '-22px'
},
margin: {
height: 100,
},
}));
The root class will contain those styles and it'll be applied without providing the styles separately from the CSS file.
Solution
I am not sure what component creates the div with "jss16" class, assume it is ExternalComponent.
You should add a custom className (assuming ExternalComponent handles this correctly):
<ExternalComponent className="myclass">
...
</ExternalComponent>
this will create a DOM like this:
<div class="jss16 myclass">
...
</div>
Sou you can create css for myclass:
.myclass {
width: calc(100% - 220px) !important;
float: right;
margin-top: -22px;
}
Explanation
ExternalComponent uses jss to dynamically generate css classes, so you cant rely on the name of the dynamically generated class. In most of the cases, components with custom classes should append props.className to the generated jss like this:
return (
<div className={jssClassname + props.className ? ' ' + props.className : ''}>
{children}
</div>
);
I couldn't reproduce the error because I had some syntax issues, so I wonder if fixing these, the build will behave correctly:
Add closing / to input
Write style's using objects, example style={{left: '0%', width: '83.3333%'}}
Update class to className
Update tabindex to tabIndex
Lastly, if that doesn't help, to make your CSS work, ie .diabMetr + span + div, rewrite it to:
.diabMetr > span + div {}
or
.diabMetr > div {}
Right now, it's not selecting the child element.
I have a simple case where I want the box2 to change background to yellow, if the box1 was hovered.
code sample:
const Box = styled.div`
height: 200px;
width: 200px;
background: blue;
`;
const Box2 = styled.div`
height: 200px;
width: 200px;
background: green;
margin-top: 20px;
${Box}:hover {
background: yellow;
}
`;
in render:
<Box>Box 1</Box>
<Box2>Box 2</Box2>
Link to the code preview:
https://stackblitz.com/edit/react-rvhgov
Thanks!
Edit This one above doesnt seem to work, dont know why, it should work?
Depend on your render items you can do it with different approaches but as things we got here, you can use adjacent sibling combinator (+) or general sibling combinator (~). So all you have to do is to replace this
${Box}:hover {
background: yellow;
}
with
/* If you want to only select the next element sibling, you should replace ~ with + */
${Box}:hover ~ & {
background: yellow
}
Working Demo:
https://stackblitz.com/edit/select-next-sibling
You can try this:
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import styled, {css} from 'styled-components';
const Box = styled.div`
height: 200px;
width: 200px;
background: blue;
`;
const Box2 = styled.div`
height: 200px;
width: 200px;
background: green;
margin-top: 20px;
${props => props.hovered && css`
background: yellow;
`}
`;
export default function App() {
const [hovered, setHovered] = useState(false);
return (
<div className="App">
{'' + hovered}
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Box onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>Box 1</Box>
<Box2 hovered={hovered}>Box 2</Box2>
</div>
);
}
render(<App />, document.getElementById('root'));
With this approach, it doesn't matter are elements/components siblings or not. It will work for siblings but also when components are nested in different subtrees.
Live example (images might load slowly): https://suhadolnik-photo.surge.sh/portreti
I'm making a photography site with GatsbyJS and using the following template as a base site that I've been changing: https://github.com/LekoArts/gatsby-starter-portfolio-emilia
Being really new to graphql I've run into a problem displaying images after a user clicks on the card to show the 'Portraits' subpage. The images are all displayed with a fixed width and height which I don't want. I need to display them with their native width and height, just resized to fit into the grid.
I've tried changing the graphql query in the project.js file, where you set the maxWidth: 1600 to no avail, as well as the resize(width: 800) further down the query. Later I found out that changing the margin on gatsby-image-wrapper through dev tools gave me the expected results, but that required changing the core gatsby-image plugin and having to manually change the margin for every image separately which isn't the solution.
project.js
import React from 'react'
import Img from 'gatsby-image'
import PropTypes from 'prop-types'
import { graphql } from 'gatsby'
import styled from 'styled-components'
import { Layout, ProjectHeader, ProjectPagination, SEO } from '../components'
import config from '../../config/site'
const BG = styled.div`
background-color: ${props => props.theme.colors.bg};
position: relative;
padding: 2rem 0 0 0;
`
const OuterWrapper = styled.div`
padding: 0 ${props => props.theme.contentPadding};
margin: -10rem auto 0 auto;
`
const InnerWrapper = styled.div`
position: relative;
max-width: ${props => `${props.theme.maxWidths.project}px`};
margin: 0 auto;
`
const Grid = styled.div`
display: grid;
grid-template-columns: repeat(${props => props.theme.gridColumnsProject}, 1fr);
grid-gap: 20px;
#media (max-width: 768px) {
grid-template-columns: 1fr;
}
`
const Project = ({ pageContext: { slug, prev, next }, data: { project: postNode, images } }) => {
const project = postNode.frontmatter
return (
<Layout customSEO>
<SEO postPath={slug} postNode={postNode} postSEO />
<ProjectHeader
name={config.name}
date={project.date}
title={project.title}
areas={project.areas}
text={postNode.body}
/>
<BG>
<OuterWrapper>
<InnerWrapper>
<Grid>
{images.nodes.map(image => (
<Img
alt={image.name}
key={image.childImageSharp.fluid.src}
fluid={image.childImageSharp.fluid}
style={{ margin: '2rem 0' }}
/>
))}
</Grid>
</InnerWrapper>
<ProjectPagination next={next} prev={prev} />
</OuterWrapper>
</BG>
</Layout>
)
}
export default Project
Project.propTypes = {
pageContext: PropTypes.shape({
slug: PropTypes.string.isRequired,
next: PropTypes.object,
prev: PropTypes.object,
}),
data: PropTypes.shape({
project: PropTypes.object.isRequired,
images: PropTypes.object.isRequired,
}).isRequired,
}
Project.defaultProps = {
pageContext: PropTypes.shape({
next: null,
prev: null,
}),
}
export const pageQuery = graphql`
query($slug: String!, $absolutePathRegex: String!) {
images: allFile(
filter: {
absolutePath: { regex: $absolutePathRegex }
extension: { regex: "/(jpg)|(png)|(tif)|(tiff)|(webp)|(jpeg)/" }
}
sort: { fields: name, order: ASC }
) {
nodes {
name
childImageSharp {
fluid(maxWidth: 1600, quality: 90) {
...GatsbyImageSharpFluid_withWebp
}
}
}
}
project: mdx(fields: { slug: { eq: $slug } }) {
body
excerpt
parent {
... on File {
mtime
birthtime
}
}
frontmatter {
cover {
childImageSharp {
resize(width: 800) {
src
}
}
}
date(formatString: "DD.MM.YYYY")
title
areas
}
}
}
`
Card.js the parent component:
import React from 'react'
import styled from 'styled-components'
import PropTypes from 'prop-types'
import { useSpring, animated, config } from 'react-spring'
import { rgba } from 'polished'
import Img from 'gatsby-image'
import { Link } from 'gatsby'
const CardItem = styled(Link)`
min-height: 500px;
position: relative;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
justify-content: flex-end;
color: ${props => props.theme.colors.color};
transition: all 0.3s ease-in-out;
&:hover {
color: white;
transform: translateY(-6px);
}
#media (max-width: ${props => props.theme.breakpoints.s}) {
min-height: 300px;
}
`
const Cover = styled.div`
width: 100%;
height: 100%;
position: absolute;
`
const Content = styled.div`
padding: 1rem;
position: relative;
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
opacity: 0;
background: ${props => rgba(props.theme.colors.link, 0.65)};
height: 0;
${CardItem}:hover & {
opacity: 1;
height: 120px;
}
`
const Bottom = styled.div`
margin-top: 0.5rem;
display: flex;
align-items: center;
font-size: 0.85rem;
div:first-child {
margin-right: 1rem;
}
`
const Name = styled.h2`
margin-bottom: 0;
margin-top: 0;
`
const Card = ({ path, cover, date, areas, title, delay }) => {
const springProps = useSpring({
config: config.slow,
delay: 200 * delay,
from: { opacity: 0, transform: 'translate3d(0, 30px, 0)' },
to: { opacity: 1, transform: 'translate3d(0, 0, 0)' },
})
return (
<animated.div style={springProps}>
<CardItem to={path}>
<Cover>
<Img fluid={cover} />
</Cover>
<Content>
<Name>{title}</Name>
<Bottom>
<div>{date}</div>
<div>
{areas.map((area, index) => (
<React.Fragment key={area}>
{index > 0 && ', '}
{area}
</React.Fragment>
))}
</div>
</Bottom>
</Content>
</CardItem>
</animated.div>
)
}
export default Card
Card.propTypes = {
path: PropTypes.string.isRequired,
cover: PropTypes.object.isRequired,
date: PropTypes.string.isRequired,
areas: PropTypes.array.isRequired,
title: PropTypes.string.isRequired,
delay: PropTypes.number.isRequired,
}
I expect the images to show in their native width and height, but resized to fit the grid. Providing visual representation below on how it looks now and what the expected result is.
Current result and expected result
Cheers!
Remove height:100% and position:absolute from your cover component on the homepage.
const Cover = styled.div`
width: 100%;
`
Also, in case you weren't aware, you can pass style and imgStyle props to Gatsby image to change it's css.
| style | object | Spread into the default styles of the wrapper element |
| imgStyle | object | Spread into the default styles of the actual img element |
| placeholderStyle | object | Spread into the default styles of the placeholder img element |
So in your project template you can change the object fit style like this:
<Img
alt={image.name}
key={image.childImageSharp.fluid.src}
fluid={image.childImageSharp.fluid}
imgStyle={{ objectFit: 'contain' }}
/>
I have a modal window and i want it to hide if user clicked on modal itself(black background), but click is trigerring by clildrens to. Here is the example:
import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
export function Modal({ show, children }) {
return (
<DivModal show={show}>
{children}
</DivModal>
)
}
const DivModal = styled.div`
display: ${props => (props.show ? 'block' : 'none')};
`
How to fire event's only for modal itself?
Ehh, this work, but if you click on wraper div - modal will not hide :(
After some googling ant try, found a way. Key point is that you need to stop onClick event propagation from parent to childrens. In my case i just wrapped my component by div with onClick={e => e.stopPropagation()}.
import React from 'react'
import styled from 'styled-components'
export function Modal({ show, showModalSet, children }) {
return (
<DivModal onClick={() => showModalSet(false)} show={show}>
<div onClick={e => e.stopPropagation()}>{children}</div>
</DivModal>
)
}
const DivModal = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: ${({ show }) => (show ? 'block' : 'none')};
z-index: 10;
overflow-y: scroll;
`
Component use:
<Modal show={showModal} showModalSet={showModalSetFunctionInUsePlace}>
Another approach, that i'am happy now with is to use refs and compare what component was clicked:
import React, { useRef } from 'react'
import styled from 'styled-components'
export function Modal({ show, showModalSet, children }) {
const modalRef = useRef(null)
function handleClick(e) {
if (e.target == modalRef.current) showModalSet(false)
}
return (
<DivModal onClick={handleClick} show={show} ref={modalRef}>
{children}
</DivModal>
)
}
const DivModal = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: ${({ show }) => (show ? 'block' : 'none')};
z-index: 10;
overflow-y: scroll;
`