Match the unspecified height of an img with another adjacent div? - javascript

This is the JSX of the component in question:
import React from 'react'
import * as classes from './PhotoCard.module.scss'
const PhotoCard = ({ img, name, username, profileImg, style }) =>
<div className={classes.PhotoCardWrapper} style={style}>
<div className={classes.ImgWrapper}>
{img}
</div>
<div className={classes.Sidebar}>
<div className={classes.Creator}>
<div className={classes.ProfilePicture}>
{profileImg}
</div>
<div className={classes.ProfileInfo}>
<h5>{name}</h5>
<p>{username}</p>
</div>
</div>
<div className={classes.Comments}>
<input type="text" name="comment" id="comment" placeholder="Write a comment" />
</div>
</div>
</div>
export default PhotoCard
Here is the relevant SCSS:
.PhotoCardWrapper {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
box-sizing: border-box;
border-radius: 5px;
box-shadow: 0 2px 6px rgba(0,0,0,0.16), 0 2px 6px rgba(0,0,0,0.2);
.ImgWrapper {
display: flex;
width: 100%;
img {
width: 100%;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
}
.Sidebar {
width: 300px;
height: 100%;
display: flex;
flex-flow: column nowrap;
.Creator {
width: 100%;
display: flex;
padding: 10px;
overflow: hidden;
overflow-x: auto;
box-sizing: border-box;
.ProfilePicture {
width: 40px;
height: 40px;
border-radius: 40px;
background-image: url('../../static/defaults/placeholder.png');
background-size: contain;
cursor: pointer;
margin-right: 10px;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
border-radius: 40px;
}
}
.ProfileInfo {
height: 40px;
display: flex;
flex-flow: column nowrap;
justify-content: center;
}
h5 {
font-weight: 500;
letter-spacing: 0.5px;
}
p {
font-size: 0.7em;
color: $color-2;
}
}
.Comments {
width: 100%;
display: flex;
flex-flow: column nowrap;
flex-grow: 1;
background: #525252;
input {
border: none;
border-top: 1px solid $color-1;
font-size: 0.9em;
padding: 5px;
}
}
}
}
To explain, I'm trying to preserve the aspect ratio of the image on the left side of the component. I can use an absolute unit for the width however I cannot use an absolute height unit as the height of the component will be based upon the aspect ratio of the image.
The "Sidebar" using flexbox will therefore have to match the height of whatever the unspecified height of the image ends up being. I cannot figure out a solution for this. As you can see I'm trying to use flex-grow on the Comments div which will not work without specifying height in parent elements.

This is the solution I found to this:
import React, { useEffect, useState } from 'react'
import * as classes from './_PhotoCard.module.scss'
import PropTypes from 'prop-types'
const PhotoCard = ({ ident, img, name, profileImg }) => {
const [height, setHeight] = useState(null)
const [imgClicked, setImgClicked] = useState(null)
useEffect(() => setHeight(document.getElementsByClassName('_PhotoCard_ImgWrapper__3S6GQ').item(ident).clientHeight), [ident])
const creatorJSX = (
<>
<div className={classes.ProfilePicture}>
{profileImg}
</div>
<div className={classes.ProfileInfo}>
<h5>{name}</h5>
</div>
</>
)
const imgClickedHandler = () => {
if (imgClicked === null) {
setImgClicked(classes.imgClicked)
document.body.style.overflow = "hidden"
} else {
setImgClicked(null)
document.body.style = "none"
}
}
return (
<div className={`${classes.PhotoCardWrapper} ${imgClicked}`} style={window.matchMedia("(min-width: 600px)").matches ? { marginBottom: 40 } : { marginBottom: 20 }}>
<div className={classes.CreatorMobile}>
{creatorJSX}
</div>
<div className={classes.ImgWrapper} onClick={() => imgClickedHandler()}>
{img}
</div>
<div className={classes.Sidebar} style={window.matchMedia("(min-width: 600px)").matches ? { height: height } : null}>
<div className={classes.SidebarTop}>
<div className={classes.Creator}>
{creatorJSX}
</div>
<div className={classes.Comments}/>
</div>
<input type="text" name="comment" id="comment" placeholder="Write a comment" />
</div>
</div>
)
}
PhotoCard.propTypes = {
ident: PropTypes.number,
img: PropTypes.element,
name: PropTypes.string,
profileImg: PropTypes.element,
}
export default PhotoCard
This could only be achieved by implementing some JS witchcraft.

Related

How to cancel the increase of each next value in the for loop by the previous one (Making a slider)

I want to make a simple slider that will work by shifting position:right
I need to shift each picture by 250px, doing it through a cycle - the first picture shifts as needed, and all the others increase on each other, and go much further. Is it possible to somehow set the same indentation values in 250px to all elements in the loop?
codepen - https://codepen.io/dxxxxxxfly/pen/KKePaXW
<body>
<div class="slider_block">
<div class="btn_left"><button>Back</button></div>
<div class="slider_main">
<div class="slider_image">
<div class="imaga">
<img src="https://avatars.mds.yandex.net/i?id=6adf61533a32e0002e109f820f2895fa-5889001-images-thumbs&n=13&exp=1" alt="" class="image_slider">
<img src="https://dasart.ru/userdata/image/08/ab/08ab193e2f04fb47d02efef43ff53ce9.jpg" alt="" class="image_slider">
<img src="https://images.satu.kz/3628818_w640_h640_obves-hamann-tycoon.jpg" alt="" class="image_slider">
<img src="https://shop.ilovevalencia.ru/251-thickbox_default/arenda-kabrioleta-porsche-991-cabrio-v-valensii-ispaniya.jpg" alt="" class="image_slider">
</div>
</div>
</div>
<div class="btn_right"><button>Next</button></div>
</div>
<script src="script/script.js"></script>
</body>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: black;
}
.slider_block {
height: 100vh;
align-items: center;
justify-content: center;
display: flex;
max-width: 1200px;
margin: 0 auto;
}
.slider_image {
display: flex;
height: 123px;
width: 349px;
overflow: hidden;
}
.image_slider {
position: relative;
display: flex;
object-fit: cover;
width: 100%;
aspect-ratio: 480/169;
}
.btn_left > button,.btn_right > button {
border: none;
padding: 10px 15px;
margin: 25px;
cursor: pointer;
border-radius: 4px;
}
.imaga {
display: flex;
}
let right = 0;
const next = document.querySelector('.btn_right');
const image = document.querySelectorAll('.image_slider')
next.addEventListener('click', () => {
for(const items of image ) {
right += 250
items.style.right = right + 'px'
continue
}
})

Cannot scroll on React App even on page overflow

I am currently working on the Body portion for a Spotify clone using ReactJS. I am fairly new to Javascript, HTML, and CSS so apologies for any oversights.
I am having a problem where I cannot scroll down to the bottom of the playlists page even when there are more tracks to be shown. I have tried adding 'overflow-y: scroll' to my CSS which renders a scroll bar but even then, the page cannot be scrolled (see right scrollbar in screenshot below). I have included my Body.jsx below.
Screenshot of Spotify clone
Body.jsx
import React, { useEffect } from 'react'
import styled from 'styled-components';
import {AiFillClockCircle} from "react-icons/ai";
import axios from 'axios';
import { useStateProvider } from '../utils/StateProvider';
import { reducerCases } from "../utils/Constants";
export default function Body() {
const[ {token, selectedPlaylistId, selectedPlaylist} ,dispatch] = useStateProvider();
useEffect(() => {
const getInitialPlaylist = async () => {
const response = await axios.get(
`https://api.spotify.com/v1/playlists/${selectedPlaylistId}`, {
headers: {
Authorization: "Bearer " + token,
"Content-Type": "application/json",
},
}
);
const selectedPlaylist = {
id: response.data.id,
name: response.data.name,
description: response.data.description.startsWith("<a") ? "" : response.data.description,
image: response.data.images[0].url,
tracks: response.data.tracks.items.map(({track}) => ({
id: track.id,
name: track.name,
artists: track.artists.map((artist) => artist.name),
image: track.album.images[2].url,
duration: track.duration_ms,
album: track.album.name,
context_uri: track.album.uri,
track_number: track.track_number,
})),
};
dispatch({type: reducerCases.SET_PLAYLIST,selectedPlaylist});
};
getInitialPlaylist();
},[token,dispatch, selectedPlaylistId]);
return (
<Container>
{
selectedPlaylist && (
<>
<div className="playlist">
<div className="image">
<img src={selectedPlaylist.image} alt="selectedplaylist" />
</div>
<div className="details">
<span className="type">PLAYLIST</span>
<h1 className="title">{selectedPlaylist.name}</h1>
<p className="description">{selectedPlaylist.description}</p>
</div>
</div>
<div className="list">
<div className="header__row">
<div className="col">
<span>#</span>
</div>
<div className="col">
<span>TITLE</span>
</div>
<div className="col">
<span>ALBUM</span>
</div>
<div className="col">
<span><AiFillClockCircle /></span>
</div>
</div>
<div className="tracks">
{
selectedPlaylist.tracks.map(( {id,name,artists,image,duration,album,context_uri,track_number},index) => {
return (
<div className="row" key={id}>
<div className="col">
<span>{index+1}</span>
</div>
<div className="col detail">
<div className="image">
<img src={image} alt="track" />
</div>
<div className="info">
<span className="name">{name}</span>
<span>{artists}</span>
</div>
</div>
<div className="col">
<span>{album}</span>
</div>
<div className="col">
<span>{duration}</span>
</div>
</div>
);
})
}
</div>
</div>
</>
)
}
</Container>
);
}
//CSS for styled components
const Container = styled.div`
overflow-y: scroll; //scroll bar appears but doesn't scroll
.playlist {
margin: 0 2rem;
display: flex;
align-items: center;
gap: 2rem;
.image {
img {
height: 15rem;
box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px;
}
}
.details {
display: flex;
flex-direction: column;
gap: 1rem;
color: #e0dede;
.title {
color: white;
font-size: 4rem;
}
}
}
.list {
.header__row {
display: grid;
grid-template-columns: 0.3fr 3fr 2fr 0.1fr;
margin: 1rem 0 0 0;
color: #dddcdc;
position: sticky;
top: 15vh;
padding: 1rem 3rem;
transition: 0.3s ease-in-out;
}
.tracks {
margin: 0 2rem;
display: flex;
flex-direction: column;
margin-bottom: 5rem;
.row {
padding: 0.5rem 1rem;
display: grid;
grid-template-columns: 0.3fr 3.1fr 2fr 0.1fr;
&:hover {
background-color: rgba(0, 0, 0, 0.7);
}
.col {
display: flex;
align-items: center;
color: #dddcdc;
img {
height: 40px;
width: 40px;
}
}
.detail {
display: flex;
gap: 1rem;
.info {
display: flex;
flex-direction: column;
}
}
}
}
}
`;
If anyone has any ideas as to how I can get the page to be scrollable, I would greatly appreciate it. I have done lots of research but nothing has worked. Thank you so much!
I was working on the same project and faced the same issue.
I figured out that the issue lied in Spotify.jsx file.
Let this be the code:
.body {
height: 100%;
width: 100%;
overflow: auto;
&::-webkit-scrollbar {
width: 0.7rem;
max-height: 2rem;
&-thumb {
background-color:rgba(255, 255, 255, 0.6);
}
}
}

How to make content scrollable inside a flex box without given the height?

I'm trying to create a flex row split panel with a footer inside a column flexbox. However, I find out that I cannot make the content to be scrollable unless I limit the height of the container by coding height: calc(100% - FooterHeight). Does anyone know an alternative way to achieve this?
Thanks in advance!
Sandbox URL
My code:
import React from "react";
import "./styles.css";
const lefts = new Array(15).fill("Left");
const rights = new Array(15).fill("Right");
export default function App() {
return (
<div className="height">
<div className="column">
<div className="row">
<div className="row-left">
{lefts.map((left, index) => {
return (
<div key={index} className="item">
{left + index}
</div>
);
})}
</div>
<div className="row-right">
{rights.map((right, index) => {
return (
<div key={index} className="item">
{right + index}
</div>
);
})}
</div>
</div>
<div className="footer">Footer</div>
</div>
</div>
);
}
My CSS:
.height {
height: 600px;
width: 600px;
background-color: gray;
display: flex;
flex-direction: column;
}
.column {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
}
.row {
flex: 1;
display: flex;
height: calc(100% - 60px); // not preferred
}
.row-left {
width: 200px;
border: 1px solid red;
overflow: auto;
}
.row-right {
flex: 1;
border: 1px solid peru;
overflow: auto;
}
.item {
padding: 16px;
}
.footer {
flex-shrink: 0;
height: 60px;
border: 1px solid blue;
}
You need to replace that height with overflow: hidden for .row:
.row {
flex: 1;
display: flex;
overflow: hidden;
}
Here is fork of your Codesandbox:
https://codesandbox.io/s/fast-wood-1wxii

TypeError: Cannot read property 'map' of undefined - React app - useContext and useReducer issue

I am using react for making an app project using contextHook and reducerHook but getting:
TypeError: Cannot read property 'map' of undefined
The same code was working a few days back, but now it's giving that error. I've wasted so many hours trying to finding answers on the internet, but I couldn't get any help... Here are pictures of the error and my code as well:
Complete code on Github repo
Error Pic 1
Error Pic 2
Error Pic 3
//App.js
import React from 'react';
import './App.css';
import Header from './Header';
import Balance from './Balance';
import AccSummary from './AccSummary';
import TransactionHistory from './Transactionhistory';
import AddTransaction from './AddTransaction';
function App() {
return (
<div className='container'>
<Header />
<Balance />
<AccSummary />
<TransactionHistory />
<AddTransaction />
</div>
);
}
export default App;
//Transaction.js
import React from 'react';
export const Transaction = ({transObj}) => {
return (
<li>
<span>{transObj.description}</span>
<span> {transObj.amount} </span>
</li>
)
}
//TransactionHistory.js
import React, { useContext } from 'react';
import { TransactionContext } from './TransContext';
import { Transaction } from './Transaction';
function TransactionHistory() {
let {transactions} = useContext(TransactionContext);
return (
<div>
<h2> History </h2>
<hr />
<ul className='transaction-list'>
{transactions.map(transObj => (
<Transaction key={transactions.id} transactions = {transactions} />
))}
//Add Transaction.js
import React from 'react';
function AddTransaction() {
return (
<div>
<h2 className='AddTrans'> <br /> Add New Transaction</h2>
<hr />
<form className = 'transaction-form'>
<label>
Enter Description <br />
<input type='text'
placeholder='Enter Detail of Transaction' className='color'/> <br />
</label>
<label>
Enter Amount <br />
<input type='Number'
placeholder='Enter Transaction Amount'/>
</label>
<br />
<input className='button' type="submit" value="Add Transaction"/>
</form>
</div>
);
}
export default AddTransaction;
//TransContezt.js
import { createContext } from "react";
const initialTransaction = [
{amount: 500, description: 'Cash'},
{amount: -200, description: 'Bill'}
]
export const TransactionContext = createContext(initialTransaction);
//App.css
.container {
width: 350px;
min-height: 700px;
margin: 0 auto ;
background-color:white;
box-shadow: 0 0 10px gray;
margin-top: 50px;
padding: 10px 50px;
font-family: "Times New Roman", Times, serif;
background-image: url("1.png"), url("2.png"), url("3.png"), url("4.png");
background-position: left top, left top, left top, left top ;
background-repeat: repeat-x;
background-size: 100% 20%, 100% 55%, 100% 75%, 100% 100%;
}
.text-centre{
text-align: center;
justify-content: space-between;
background-color: #98DBC6;
border-radius: 5px;
display: grid;
justify-content: space-around;
}
.accsumm{
display: flex;
justify-content: space-around ;
background-color: #5BC8AC;
box-shadow: 0 0 10px gray;
border: thin;
font-family: Arial, Helvetica, sans-serif;
border-radius: 5px;
}
.AddTrans{
margin: 10px 0 0;
}
.transaction-form input{
width: 97%;
margin: 10px 0;
padding: 15px 5px;
border-radius: 5px;
color: white;
}
.color{
color: white;
}
.transaction-list{
margin: 0;
padding: 0;
}
.transaction-list > li{
display: flex;
justify-content: space-between;
box-shadow: 0 0 10px gray;
padding: 10px 5px;
background-color: honeydew;
margin: 5px 0;
border-radius: 5px;
}
.button {
width: 100%;
display: flex;
justify-content: space-around;
box-shadow: 0 0 10px gray;
padding: 10px 10px;
background-color: rgb(138, 179, 255);
margin: 10px 0;
border-radius: 5px;
font-size: larger;
font-weight: bolder;
align-self: center;
}
Tr
For the TransactionHistory component:
The default context value is an array but you destructure it like an object.
In the mapping function, you are passing the original array as a prop instead of the element.
Here is an updated version:
function TransactionHistory() {
const transactions = useContext(TransactionContext);
return (
<div>
<h2> History </h2>
<hr />
<ul className="transaction-list">
{transactions.map((transObj, index) => (
<Transaction key={index} transaction={transObj} />
))}
</ul>
</div>
);
}
Then, in the Transaction component, it is expecting transObj prop but its parent passes in transaction prop. So you might fix it as well:
import React from "react";
const Transaction = ({ transObj }) => {
return (
<li>
<span>{transObj.description}</span>
<span> {transObj.amount} </span>
</li>
);
};

Content (pictures) not showing in modal component vuejs2

I'm quite new in vue and frontend generally. I've created component to show and like/dislike pictures. I planned on using it both on main app view and in modal which shows user liked ones. I use exactly the same code in both cases, but nothing shows on the modal. I can see that the Set with pictures is passed from main app to modal, but the pictures are not visible. Here is my code. It's probably some silly mistake, but i have nobody to ask about it.
Shibe.vue (picture component)
<template>
<div >
<div>
<img :src="shibe">
</div>
<div v-bind:class="{ liked : isFavoured }">
<button type="button" class="btn btn-danger"
#click="addToFavourites(shibe, $emit('liked', shibe))"
>
<font-awesome-icon icon="thumbs-up"/>
</button>
<button type="button" class="btn btn-dark"
#click="removeFromFavourites(shibe, $emit('unliked', shibe))"
style="float: right">
<font-awesome-icon icon="thumbs-down"/>
</button>
</div>
</div>
</template>
<script>
export default {
name: "shibe",
props: {
favoured: {default: false},
shibe: '',
},
data: function () {
return {
isFavoured: false,
modal: false,
}
},
mounted() {
this.isFavoured = this.favoured;
},
methods: {
addToFavourites() {
this.isFavoured = true;
},
removeFromFavourites() {
this.isFavoured = false;
}
},
}
</script>
<style lang="scss">
/* Optional Styles */
.header {
position: fixed;
top: 10px;
right: 10px;
width: 10%;
height: 20px;
text-align: right;
}
.liked {
background: firebrick;
}
</style>
App.vue
<template>
<div id="app">
<nav class="navbar navbar-light bg-light">
<span class="navbar-brand mb-0 h1">
<button type="button" class="btn btn-primary" #click="showModal()">
Favourites
</button>
</span>
</nav>
<div class="doge" v-for="shibe in shibes">
<shibe :shibe=shibe #liked="addToFavourited" #unliked="removeFromFavourited"></shibe>
</div>
<favs :favourites=favouriteShibes
v-show="isModalVisible"
#close="closeModal"></favs>
</div>
</template>
<script>
import axios from 'axios';
import 'bootstrap/dist/css/bootstrap.css';
import Favs from "./Favs";
import Shibe from "./Shibe";
export default {
name: 'app',
components: {Favs, Shibe},
data() {
return {
shibes: new Set(),
favouriteShibes: new Set(),
isModalVisible: false,
}
},
methods: {
getInitialShibes() {
axios.get(`http://shibe.online/api/shibes?count=12`)
.then(response => {
this.shibes = response.data;
});
},
addToFavourited(shibe) {
this.favouriteShibes.add(shibe);
console.log(this.favouriteShibes);
},
removeFromFavourited(shibe) {
this.favouriteShibes.delete(shibe);
console.log(this.favouriteShibes);
},
scroll() {
window.onscroll = () => {
let bottomOfWindow = document.documentElement.scrollTop + window.innerHeight === document.documentElement.offsetHeight;
if (bottomOfWindow) {
for (let i = 0; i < 4; i++) {
axios.get(`http://shibe.online/api/shibes?count=1`)
.then(response => {
this.shibes.push(response.data);
});
}
}
}
},
showModal() {
this.isModalVisible = true;
},
closeModal() {
this.isModalVisible = false;
}
},
mounted() {
this.scroll();
},
beforeMount() {
this.getInitialShibes();
}
}
</script>
<style lang="scss">
.header {
position: fixed;
top: 10px;
right: 10px;
width: 10%;
height: 20px;
text-align: right;
}
.doge {
border-radius: 2px;
width: 25%;
height: 20%;
margin: 0 auto 15px auto;
padding: 15px;
display: inline-block;
flex-wrap: wrap;
img {
width: 100%;
height: 100%;
border-radius: 2px;
}
}
.doge-div {
width: 25%;
align-items: center;
}
</style>
Favs.vue (Modal component)
<template>
<div class="modal-backdrop">
<div class="modal">
<header class="modal-header">
<slot name="header">
<button #click="console()">Favourite shibes
</button>
<button
type="button"
class="btn-close"
#click="close"
>
x
</button>
</slot>
</header>
<section class="modal-body">
<slot>
<div class="doge" v-for="shibe in favourites">
<shibe :shibe=shibe></shibe>
</div>
</slot>
</section>
</div>
</div>
</template>
<script>
import Shibe from "./Shibe";
export default {
name: "favs",
components: {Shibe},
props: {
favourites: {},
},
methods: {
close() {
this.$emit('close');
},
console() {
console.log(this.favourites);
}
},
}
</script>
<style lang="scss">
.modal-backdrop {
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
justify-content: center;
align-items: center;
}
.modal {
background: #FFFFFF;
box-shadow: 2px 2px 20px 1px;
overflow-x: auto;
display: flex;
flex-direction: column;
width: 80%;
height: 60%;
position: absolute;
top: 30%;
left: 50%;
transform: translate(-50%, -30%);
}
.modal-header,
.modal-footer {
padding: 15px;
display: flex;
}
.modal-header {
border-bottom: 1px solid #eeeeee;
color: #4AAE9B;
justify-content: space-between;
}
.modal-body {
position: relative;
padding: 20px 10px;
}
.btn-close {
border: none;
font-size: 20px;
padding: 20px;
cursor: pointer;
font-weight: bold;
color: #4AAE9B;
background: transparent;
}
</style>

Categories

Resources