I am building a "flash card" component that reveals the backplate on hover. The frontplate will hold an image. I am trying to adapt the class on the image to adapt it for a landscape/portrait type container size so the image always fills the space. Although when the page loads for the first time - the width is coming back as 0. On redirect - the image class doesn't feel correct and fails to fill the space - with the grey bit at the bottom on the right. Looking to make sure the code logic is correct and a way to fix the issue.
css
.flash-card{
width: 100%;
height: 300px;
overflow:hidden;
background: pink;
.show{
display:block;
}
.hide{
display:none;
}
.flash-card-contents{
width: 100%;
height: 100%;
min-height: 100vh;
font-size: 12px;
&.frontcard{
background: $grey;
position: relative;
.flash-img{
width: 100%;
}
}
&.backcard{
background: $white;
position: relative;
padding: 25px;
}
}
&.portrait {
.flash-card-contents.frontcard{
.flash-img{
width: auto;
height: 100%;
}
}
}
&.landscape {
.flash-card-contents.frontcard{
.flash-img{
width: 100%;
height: auto;
}
}
}
}
js
import React, { Component } from 'react';
import Grid from '#material-ui/core/Grid';
import Button from '#material-ui/core/Button';
import './FlashCard.scss';
class FlashCard extends Component {
constructor() {
super();
this.myRef = React.createRef();
this.state = {
isFlashed: false,
orientation: ""
};
this.handleToggle = this.handleToggle.bind(this);
this.updateDimensions = this.updateDimensions.bind(this);
}
handleToggle(e) {
e.preventDefault();
this.setState(prevState => ({ isFlashed: !prevState.isFlashed }));
}
componentDidMount() {
window.addEventListener("resize", this.updateDimensions);
this.updateDimensions();
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateDimensions);
}
updateDimensions(){
console.log("----->>flash width", this.myRef.current.getBoundingClientRect().width);
console.log("----->>flash height", this.myRef.current.getBoundingClientRect().height);
//update the dimensions of the slide for responsiveness
if (this.myRef.current.getBoundingClientRect().width > this.myRef.current.getBoundingClientRect().height){
//it's a landscape
this.setState({
orientation: "landscape"
});
} else if (this.myRef.current.getBoundingClientRect().width < this.myRef.current.getBoundingClientRect().height){
//it's a portrait
this.setState({
orientation: "portrait"
});
} else {
//image width and height are equal, therefore it is square.
this.setState({
orientation: "square"
});
}
}
render() {
return (
<div
ref={this.myRef}
className={"flash-card " + this.state.orientation}
style={{
height: (this.props.height? this.props.height: "auto")
}}
>
<div className={this.state.isFlashed? 'hide': 'show'} onMouseOver={this.handleToggle}>
<div className="flash-card-contents frontcard">
<img className="flash-img" src={this.props.image} alt="" />
</div>
</div>
<div className={this.state.isFlashed? 'show': 'hide'} onMouseLeave={this.handleToggle}>
<div className="flash-card-contents backcard">
<Grid container spacing={1}>
<Grid item xs={12} sm={12}>
<h3>{this.props.header}</h3>
<h4>{this.props.subheader}</h4>
</Grid>
<Grid item xs={12} sm={12}>
{this.props.body}
</Grid>
<Grid item xs={12} sm={12}>
<Button
className="flash-card-button"
variant="contained"
color="primary"
href={this.props.button.link}
>
{this.props.button.label}
</Button>
</Grid>
</Grid>
</div>
</div>
</div>
);
}
}
export default FlashCard;
Related
In a react app, I dynamically create new components inside of a container. When the height of the container is exceeded by the newly created items, I want the container to become scrollable in the Y direction. I could not achieve the behavior by using overflow: 'auto'. Any suggestions?
sass:
.v-timeline{ // parent component
flex: 1;
height: 100%;
width: 100%;
flex-wrap: nowrap;
.v-timeline-container { // this is our container
.v-timeline-indicator {
z-index: 2;
background-color: rgb(0, 105, 185);
width: 3px;
position: relative;
}
.v-timeline-items { // these are the new items
.v-timeline-item {
height: 60px;
}
}
}
}
The component:
const [gridItems, setGridItems] = useState([{ id: 'firstItemID', info: 'firstItemInfo'}]);
const addRow = () => {
setGridItems([
...gridItems, {
id: uuidv4() ,
info: 'doesnt matter'
}
]);
}
return (
<>
<button onClick={addRow}>Add Row</button>
<div className='v-timeline-container' style={{ overflow: 'auto'}} >
{
gridItems.map(item => (
<div id={`thisTimeline${item.id}`} style={{ display: 'flex', overflow: 'scroll'}} key={`thisItem${item.id}`} className="v-timeline-items v-border">
<Indicator id={item.id} height={60}/>
<div>
<CustomItemContainer id={item.id} info={item.info}/>
</div>
</div>
))
}
</div>
</>
)
This is how it starts:
This is how it goes:
It seems like you didn't set height to the container.
Try to set a fixed height to container.
.v-timeline-container{
height: 100%;
}
I am using react-scrollmagic for scrolling effect.
When I use its component, there is a prop called progress,
{(progress, event) => (
…
)}
Which I want to update my state currentProgress by.
How can I do it?
App.js
import "./styles.css";
import styled from "styled-components";
import { Controller, Scene } from "react-scrollmagic";
import React, { useState, useEffect } from "react";
const ClassToggleStyled = styled.div`
.section {
height: 100vh;
}
.test {
transition: width 0.3s ease-out;
width: 100px;
height: 100px;
background-color: red;
margin: 0 !important;
&.yellow {
background-color: yellow;
}
}
.zap {
width: 100%;
}
`;
export default function App() {
const [currentProgress, setCurrentProgress] = useState(0);
useEffect(() => {
// I want to update the currentProgress whenever the progress changed becausing of scrolling
setCurrentProgress(0);
}, []);
return (
<ClassToggleStyled>
<div style={{ position: "fixed", top: 0 }}>
Current Progress: {currentProgress}
</div>
<div className="section" />
<div id="trigger" />
<Controller>
<Scene
duration={200}
classToggle="zap"
triggerElement="#trigger"
indicators={true}
>
{(progress, event) => (
<div className="test">
<div style={{ position: "fixed", top: 30 }}>
Progress: {progress}
</div>
Pin Test {event.type} {progress}
</div>
)}
</Scene>
<Scene
classToggle={[".test", "yellow"]}
reverse={false}
indicators={true}
>
<div>Toggle other class</div>
</Scene>
</Controller>
<div className="section" />
</ClassToggleStyled>
);
}
Codesandbox
https://codesandbox.io/s/nifty-hermann-byrvz?file=/src/App.js
Update 1
I updated the source code based on comment.
And I added a button to update currentProgress but I find it not working.
Error in Console
Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.
Warning: Cannot update a component (`App`) while rendering a different component (`SceneBase`). To locate the bad setState() call inside `SceneBase`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
App.js
import "./styles.css";
import styled from "styled-components";
import { Controller, Scene } from "react-scrollmagic";
import React, { useState, useEffect } from "react";
const ClassToggleStyled = styled.div`
.section {
height: 100vh;
}
.test {
transition: width 0.3s ease-out;
width: 100px;
height: 100px;
background-color: red;
margin: 0 !important;
&.yellow {
background-color: yellow;
}
}
.zap {
width: 100%;
}
`;
export default function App() {
const [currentProgress, setCurrentProgress] = useState(0);
return (
<ClassToggleStyled>
<button
onClick={() => {
console.log("update progress to 0.5");
setCurrentProgress(0.5);
}}
style={{ position: "fixed", top: 50 }}
>
update currentProgress
</button>
<div style={{ position: "fixed", top: 0 }}>
Current Progress: {currentProgress}
</div>
<div className="section" />
<div id="trigger" />
<Controller>
<Scene
duration={200}
classToggle="zap"
triggerElement="#trigger"
indicators={true}
>
{(progress, event) => {
setCurrentProgress(progress);
return (
<div className="test">
<div style={{ position: "fixed", top: 30 }}>
Progress: {progress}
</div>
Pin Test {event.type} {progress}
</div>
);
}}
</Scene>
<Scene
classToggle={[".test", "yellow"]}
reverse={false}
indicators={true}
>
<div>Toggle other class</div>
</Scene>
</Controller>
<div className="section" />
</ClassToggleStyled>
);
}
Codesandbox
https://codesandbox.io/s/great-noether-dtfok?file=/src/App.js
You can set the state in the library's event handler:
{(progress, event) => {
setCurrentProgress(progress);
return (
<div className="test">
<div style={{ position: "fixed", top: 30 }}>
Progress: {progress}
</div>
Pin Test {event.type} {progress}
</div>
)}
}
Updated sandbox
I am trying to move individual components with inline styles as I want them in different places of the website. Why is this code not working?
import "./App.css";
import React from "react";
import Window from "./components/Window";
class App extends React.Component {
render() {
return (
<div className="App">
<Window
id="firstWindow"
style={{ position: "relative", left: "200px" }}
number={"1"}
/>
<Window id="secondWindow" number={"2"} />
<Window id="thirdWindow" number={"3"} />
</div>
);
}
}
export default App;
this code works in a different section of the app
import React from "react";
import "./Window.css";
class Window extends React.Component {
render() {
return (
<div
className="square"
style={{ position: "relative", left: "200px" }}
>
{this.props.number}
</div>
);
}
}
export default Window;
Inline style is passed by property name style, use it to set style in the component.
style={ this.props.style }
A demo:
class App extends React.Component {
render() {
return (
<div className="App">
<Window
id="firstWindow"
style={{ position: "relative", left: "200px" }}
number={"1"}
/>
<Window
id="secondWindow"
style={{ position: "relative", left: "100px" }}
number={"2"} />
<Window
id="thirdWindow"
style={{ position: "relative", left: "10px" }}
number={"3"} />
</div>
);
}
}
class Window extends React.Component {
render() {
return (
<div
className="square"
style={ this.props.style }
>
{this.props.number}
</div>
);
}
}
// ========================================
ReactDOM.render(
<App />,
document.getElementById('root')
);
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
So here's a code sandbox with a working example based on your code-
https://codesandbox.io/s/strange-borg-hd2o4?file=/src/App.js
First of all, you need to be using position: absolute if you're going to use left: 200px. When it's position: relative, there's no effect.
Secondly, a better way to style these is to use the ids you've already created and then pass it as a prop to the component, check out the example.
Thirdly, React class components are on the way out - you should be learning the React function approach first. I recommend https://fullstackopen.com/en/.
Hopefully that all makes sense!
I am a little bit confused, when I am passing a method defined in the parent component, that is changing the view of the parent component down to a child component (e.g to a button in the child component), and when I click the button on the child component, does the method from parent component gets executed in the parent class (and therefore, on the parent view) or on the child class (and then on the child view)?
lets say I want to display an alert message on the parent class when the button in the child class gets clicked, the alert message will get displayed on the parent I guess.
But is it also possible to pass a method defined in the parent down to the child and then change some views in the child component?
EDIT:
I give an example for my problem:
I have a component to render conditionally in Parent Class:
I pass the method onShowAlert to show an alert on parent component
if (this.state.alleAnzeigen.length > 0) {
anzeigenListe = (
<AnzeigenComponent
anzeigenArray={this.state.alleAnzeigen}
onClickAlert={this.onShowAlert}
onButtonFinish={this.finishDocumentHandler}
/>
)
In my AnzeigenComponent, I pass the method down to Anzeige Component,
first, I had onClickalert={() => props.onClickAlert} without the (), however, in my Parent the method was not executed then.
const anzeigenArray = (props) => {
return props.anzeigenArray.map((anzeige,index) => (
<li className="mainList" key={anzeige.id} style={{padding: "10px"}} >
<Anzeige
anzeige={anzeige}
key={anzeige.id}
index={index}
onClickalert={() => props.onClickAlert()}
onButtonfinish={() => props.onButtonFinish(anzeige,index)}
/>
</li>
))
}
export default anzeigenArray;
my SubComponent "Anzeige" however, is a bigger Stateful Component Class:
when I click the button inside the singleAnzeige render function, I execute props.onClickalert()-> the method that I have passed down as props. However, the method doesn't do anything, unless I already execute that method with parentheses "()" in the component that I mentioned one above, I am just wondering, why is that so? Is there a limit of subcomponents where I can pass the method down only 2 levels or so, so that it still works?
import React, {Component} from 'react';
import DubletteComponent from '../components/DubletteComponent';
import { Button, Alert } from 'reactstrap';
import { Container, Row, Col } from 'reactstrap';
class Anzeige extends Component {
constructor(props) {
super(props);
}
singleAnzeige = (props) => {
// let newText = this.htmlspecialchars_decode("71065 Sindelfingen71032 Böblingen75365 Calw72202 Nagold71083 Herrenberg71229 Leonberg");
// console.log(newText);
return (
<Row>
<Col xs={12} md={2}><b>ID:</b> {props.anzeige.id}</Col>
<Col xs={12} md={3}><b>Titel:</b> {props.anzeige.title}</Col>
<Col xs={12} md={3}><b>Institution:</b> {props.anzeige.institution}</Col>
<Col xs={12} md={2}><b>Ort:</b> {props.anzeige.ort}</Col>
<Col xs={12} md={2} className="linkButton">
Link
<button className="finishButton" onClick = {
() => {
if (window.confirm('Are you sure you wish to finish this document?')) {
props.onButtonfinish(props.anzeige,props.index);
props.onClickalert();
}
}
}>fertig</button>
</Col>
<style jsx>
{`
p, a {
}
.linkButton {
flexDirection: 'row',
justifyContent: 'flex-end',
}
.anzeigeLink {
}
.finishButton:hover {
background-color: green;
}
.finishButton {
float: right;
border: 1px solid blue;
border-radius: 10px;
background-color: dark-green;
}
#media(max-width: 576px){
.finishButton {
float: none;
margin-right: 30px;
margin-left: 20px;
}
}
`}
</style>
</Row>
);
}
render() {
return (
<div className="anzeigeCard">
{this.singleAnzeige(this.props)}
<DubletteComponent className="dublette" anzeigeID={this.props.anzeige.id} onSendDoubletten = {this.props.onClickAlert}/>
<style jsx>
{`
.anzeigeCard {
border-radius: 10px;
padding: 10px;
margin: 5px 0px 5px 0px;
border: 1px solid light-green;
width: 100%;
box-shadow: 2px 2px 2px 2px;
}
`}
</style>
</div>
)
}
}
export default Anzeige
Is there a limit of subcomponents where I can pass the method down
only 2 levels or so, so that it still works?
There is no limit on how many levels you want to to pass your method as a prop,
if (this.state.alleAnzeigen.length > 0) {
anzeigenListe = (
<AnzeigenComponent
anzeigenArray={this.state.alleAnzeigen}
onClickAlert={this.onShowAlert}
onButtonFinish={this.finishDocumentHandler}
/>
)
}
const anzeigenArray = (props) => {
return props.anzeigenArray.map((anzeige,index) => (
<li className="mainList" key={anzeige.id} style={{padding: "10px"}} >
<Anzeige
anzeige={anzeige}
key={anzeige.id}
index={index}
onClickalert={props.onClickAlert}
onButtonfinish={props.onButtonFinish}
/>
</li>
))
}
export default anzeigenArray;
import React, {Component} from 'react';
import DubletteComponent from '../components/DubletteComponent';
import { Button, Alert } from 'reactstrap';
import { Container, Row, Col } from 'reactstrap';
class Anzeige extends Component {
constructor(props) {
super(props);
}
singleAnzeige = (props) => {
// let newText = this.htmlspecialchars_decode("71065 Sindelfingen71032 Böblingen75365 Calw72202 Nagold71083 Herrenberg71229 Leonberg");
// console.log(newText);
return (
<Row>
<Col xs={12} md={2}><b>ID:</b> {props.anzeige.id}</Col>
<Col xs={12} md={3}><b>Titel:</b> {props.anzeige.title}</Col>
<Col xs={12} md={3}><b>Institution:</b> {props.anzeige.institution}</Col>
<Col xs={12} md={2}><b>Ort:</b> {props.anzeige.ort}</Col>
<Col xs={12} md={2} className="linkButton">
Link
// I couldn't verify this code, but I guess your "if" implementation may cause some error, so in order to help you investigate easier, I remove the "if" here
<button className="finishButton" onClick = {
() => props.onButtonfinish(props.anzeige,props.index);
}>fertig</button>
</Col>
<style jsx>
{`
p, a {
}
.linkButton {
flexDirection: 'row',
justifyContent: 'flex-end',
}
.anzeigeLink {
}
.finishButton:hover {
background-color: green;
}
.finishButton {
float: right;
border: 1px solid blue;
border-radius: 10px;
background-color: dark-green;
}
#media(max-width: 576px){
.finishButton {
float: none;
margin-right: 30px;
margin-left: 20px;
}
}
`}
</style>
</Row>
);
}
render() {
return (
<div className="anzeigeCard">
{this.singleAnzeige(this.props)}
<DubletteComponent className="dublette" anzeigeID={this.props.anzeige.id} onSendDoubletten = {this.props.onClickAlert}/>
<style jsx>
{`
.anzeigeCard {
border-radius: 10px;
padding: 10px;
margin: 5px 0px 5px 0px;
border: 1px solid light-green;
width: 100%;
box-shadow: 2px 2px 2px 2px;
}
`}
</style>
</div>
)
}
}
export default Anzeige
I'm using react. Material-ui is for Cards. For Grid I'm using CSS Grid Layout. So far it looks like this:
But my goal is something like this:
And I have 2 problems:
I want to have all these cards the same height (415px). I tried height: 415px on .BeerListingScroll-info-box but it doesn't work.
Images of bottles and kegs are diffrent in size [keg (80px x 160px) vs. bottle (80px x 317px)]. Is there any way to make them more similar in rendered size?
-
Code:
BeerListingScroll
import React, { Component } from 'react';
import ReduxLazyScroll from 'redux-lazy-scroll';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchBeers } from '../../actions/';
import BeersListItem from '../../components/BeersListItem';
import ProgressIndicator from '../../components/ProgressIndicator';
import './style.css';
class BeerListingScroll extends Component {
constructor(props) {
super(props);
this.loadBeers = this.loadBeers.bind(this);
}
loadBeers() {
const { skip, limit } = this.props.beers;
this.props.fetchBeers(skip, limit);
}
render() {
const { beersArray, isFetching, errorMessage, hasMore } = this.props.beers;
return (
<div className="container beers-lazy-scroll">
<ReduxLazyScroll
isFetching={isFetching}
errorMessage={errorMessage}
loadMore={this.loadBeers}
hasMore={hasMore}
>
<div className="BeerListingScroll-wrapper">
{beersArray.map(beer => (
<div key={beer.id} className="BeerListingScroll-info-box">
<BeersListItem beer={beer} />
</div>
))}
</div>
</ReduxLazyScroll>
<div className="row beers-lazy-scroll__messages">
{isFetching && (
<div className="alert alert-info">
<ProgressIndicator />
</div>
)}
{!hasMore &&
!errorMessage && (
<div className="alert alert-success">
All the beers has been loaded successfully.
</div>
)}
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
beers: state.beers,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchBeers }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(BeerListingScroll);
BeerListingScroll css
.BeerListingScroll-wrapper {
display: grid;
margin: 0;
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr) ) ;
background-color: #f7f7f7;
}
.BeerListingScroll-info-box {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
width: 320px;
}
/* This applies from 600px onwards */
#media (min-width: 1820px) {
.BeerListingScroll-wrapper {
margin: 0 400px;
}
}
#media (min-width: 1620px) {
.BeerListingScroll-wrapper {
margin: 0 300px;
}
}
#media (min-width: 1366px) {
.BeerListingScroll-wrapper {
margin: 0 200px;
}
}
BeerListItem is the child of BeerListingScroll
import React from 'react';
import Card, { CardContent } from 'material-ui/Card';
import Typography from 'material-ui/Typography';
function BeerListItem(props) {
return (
<div>
<Card raised>
<CardContent>
<img src={props.beer.image_url} alt="beer" width="30%" />
<Typography variant="headline" component="h2">
{props.beer.name}
</Typography>
<Typography component="p">{props.beer.tagline}</Typography>
</CardContent>
</Card>
</div>
);
}
export default BeerListItem;
Full project on github -> Github
So for image sizes here I got great answer.
And I added:
.BeerListItem-img {
height: auto;
max-height: 250px;
width: auto;
max-width: 250px;
}
And for card size I just added inside BeerListItem class to Card like so (.BeerListItem-main-card):
function BeerListItem(props) {
return (
<div>
<Card raised className="BeerListItem-main-card">
<CardContent>
<img
src={props.beer.image_url}
alt="beer"
className="BeerListItem-img"
/>
<Typography variant="headline" component="h2">
{props.beer.name}
</Typography>
<Typography component="p">{props.beer.tagline}</Typography>
</CardContent>
</Card>
</div>
);
}
And here is corresponding css to that component.
.BeerListItem-main-card {
width: 320px;
height: 415px;
}
.BeerListItem-img {
height: auto;
max-height: 250px;
width: auto;
max-width: 250px;
}
With that two changes, I've managed to achieve my goal.
You should try exploring display:flex;
Here is a link to a fantastic code pen that may help you achieve what you want:
https://codepen.io/enxaneta/full/adLPwv
More specifically here is an example I've created with what you might be trying to achieve.
https://jsfiddle.net/dalecarslaw/sxdr3eep/
Here is the areas of code you should focus on:
display:flex;
align-items:space-between;
justify-content:space-between;
flex-wrap:wrap;