How can resize boxes, if I remove 2 of them? - javascript

How can resize boxes, if I remove 2 of them?
For example, I have 6 boxes, and I remove 2 of them, the other 4 should change their width.
How can I do this, any suggestions?

you can use flex:
.box{
background-color:gray;
height:150px;
margin:25px;
flex:1 1 0;
}
.container{
display:flex;
flex-wrap:wrap;
}
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
</div>

You can use grid, flexbox or even set width manually. Everything is possible in React world. Well, for the last one you have to be quite tricky!
Example of each approach
const { useState, useEffect, useRef } = React;
const getItems = () => Promise.resolve(Array(6).fill(0).map((pr, index) => ({id: index})))
const random = () => Boolean(Math.floor(Math.random() * 1.4))
const App = () => {
const [items, setItems] = useState([]);
const thirdContainerRef = useRef(null);
const [thirdContainerSize, setThirdContainerSize] = useState({
width: 0,
height: 0
});
useEffect(() => {
let isUnmounted = false;
let handle = null;
getItems()
.then(items => {
if(!isUnmounted) {
setItems(items);
}
})
const loop = () => {
getItems()
.then(items => {
if(!isUnmounted) {
let filtered = items.slice(0, 4);
if(random()) {
filtered = items.slice(0, 6);
}
setItems(filtered);
}
})
handle = setTimeout(loop, 1000);
}
handle = setTimeout(loop, 1000);
return () => {
clearTimeout(handle);
isUnmounted = true;
}
}, [])
const computeFlex = () => {
const flexBasis = items.length === 6 ? 25 : 33;
return {
flex: `1 1 ${flexBasis}%`
}
}
const computeGrid = () => {
const numberOfcolumns = items.length === 6 ? 3 : 2;
return {
gridTemplateColumns: `repeat(${numberOfcolumns}, auto)`
}
}
useEffect(() => {
if(thirdContainerRef && thirdContainerRef.current) {
const element = thirdContainerRef.current;
setThirdContainerSize(element.getBoundingClientRect());
}
}, [thirdContainerRef])
const computeSize = () => {
if(!thirdContainerSize.width) {
return {}
}
const containerWidth = thirdContainerSize.width;
const containerHeight = thirdContainerSize.height;
const margin = 5 * 2;
const width = items.length === 6 ? (containerWidth / 3) - margin : (containerWidth / 2) - margin;
const height = containerHeight / 2 - margin;
return {
width: `${width}px`,
height: `${height}px`,
float: `left`
}
}
return <div>
Flexbox
<div className="container">
{items.map(pr => <div key={pr.id} style={computeFlex()} className="item"></div>)}
</div>
Grid
<div style={computeGrid()} className="second-container">
{items.map(pr => <div key={pr.id} className="item"></div>)}
</div>
Width
<div ref={thirdContainerRef} className="third-container">
{items.map(pr => <div style={computeSize()} key={pr.id} className="item"></div>)}
</div>
</div>
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
html, body {
height: 100%;
box-sizing: border-box;
margin: 0;
}
.container {
width: 100%;
height: 100px;
display: flex;
flex-wrap: wrap;
}
.second-container {
width: 100%;
height: 100px;
display: grid;
}
.third-container {
width: 100%;
height: 100px;
}
.item {
background: gray;
margin: 5px;
}
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>

Related

Drag-and-Drop Functionality in React Without Animations

I am currently in the process of replicating some of the core functionalities that exists in Trello. One of the functionalities i am looking into is the drag-and-drop.
I have managed to implement the functionality, albeit with an issue. When you drag a card from one column to another column, there is some form of animation (see the linked YouTube video for reference). To describe it in words, the animation portray a movement back to the starting position of the dragged card, although it is not the case.
https://www.youtube.com/watch?v=wSoaaKLj3r0
My current code:
/* Column.js */
import React, {useState, useRef} from 'react';
import './Column.css';
function Column({data}) {
const [list, setList] = useState(data);
const [dragging, setDragging] = useState(false);
const dragItem = useRef();
const dragNode = useRef();
const handleDragStart = (event, params) => {
console.log('drag starting', params)
dragItem.current = params;
dragNode.current = event.target;
dragNode.current.addEventListener('dragend', handleDragEnd)
setTimeout(() => {
setDragging(true);
}, 0)
}
const handleDragEnter = (event, params) => {
console.log('entering drag', params);
const currentItem = dragItem.current;
if(event.target !== dragNode.current) {
console.log('TARGET IS NOT THE SAME')
setList(oldList => {
let newList = JSON.parse(JSON.stringify(oldList));
newList[params.groupIndex].items.splice(params.itemIndex, 0, newList[currentItem.groupIndex].items.splice(currentItem.itemIndex,1)[0])
dragItem.current = params
return newList;
})
}
}
const handleDragEnd = () => {
console.log('Ending Drag')
setDragging(false);
dragNode.current.removeEventListener('dragend', handleDragEnd)
dragItem.current = null;
dragNode.current = null;
}
const getStyles = (params) => {
const currentItem = dragItem.current;
if (currentItem.groupIndex === params.groupIndex && currentItem.itemIndex === params.itemIndex) {
return 'current drag-and-drop-item';
}
return 'drag-and-drop-item'
};
return (
<section className="drag-and-drop-container">
<div className="drag-and-drop">
{list.map((group, groupIndex) => (
<div
key={group.title}
className="drag-and-drop-group"
onDragEnter={dragging && !group.items.length
? (event) => handleDragEnter(event, {groupIndex, itemIndex:0})
: null
}
>
<div className="drag-and-drop-group-title">{group.title}</div>
{group.items.map((item, itemIndex) => (
<div
draggable
onDragStart={(dragEvent) => {handleDragStart(dragEvent, {groupIndex, itemIndex})}}
onDragEnter={dragging
? (event) => {handleDragEnter(event, {groupIndex, itemIndex})}
: null
}
key={item}
className={dragging
? getStyles({groupIndex, itemIndex})
: "drag-and-drop-item"
}
>
{item}
</div>
))}
</div>
))}
</div>
</section>
)
}
export default Column;
/* Column.css */
#import url(./../../index.css);
.drag-and-drop{
display: grid;
gap: 0.5rem;
width: 100%;
height: 100%;
grid-template-columns: repeat(auto-fill, 300px);
background-color: var(--primary-color);
padding: 0.5rem;
align-items: start;
}
.drag-and-drop-group{
background-color: var(--secondary-color-80);
padding: 0.5rem;
}
.drag-and-drop-group-title{
margin-bottom: 0.5rem;
font-size: 1.2rem;
font-family: 'Poppins';
}
.drag-and-drop-item {
background-color: #FFFFFF;
color: var(--primary-color);
min-height: 150px;
}
.drag-and-drop-item:not(:last-of-type){
margin-bottom: 0.5rem;
}
.current{
background-color: var(--primary-color);
}
Findings:
The problem occurs exclusively on my MacBook, and therefore not on my Windows computer.
I have tried with Event.preventDefault();, however without luck.
Question:
What can I do to prevent the animation on Mac OS?

By using React framer motion "drag and reorder" I can drag the card but does not stick to the new position. After dragging it, return to the original

I am trying to achieve this: https://codesandbox.io/s/framer-motion-2-drag-to-reorder-fc4rt?file=/src/use-position-reorder.js
Basically you can drag a card, place it somewhere in the container and reorder the card based on where you drag/put it from the original place.
The problem is that in my code, you can drag the card but not place it. I tried to change the code so many times but I can't find a way out of it. Below you will find the code where I render the component FavouriteTreeNodeComponent which is a LI(List) nested in FavouriteTreeNodesList a UL(Unordered List) :
// react
import { useRef, useEffect, useCallback, useState } from 'react';
// animation spring
import { animated, Transition, useSpring } from 'react-spring';
import { AnimateSharedLayout } from 'framer-motion';
// styled components
import styled from 'styled-components';
// framer motion
import { motion } from 'framer-motion';
import { usePositionReorder } from './ReorderingModalNodes/use-position-reorder';
// icons
import { MdClose } from 'react-icons/md';
// components
import TemplateItem from './TemplateItem';
import SearchBar from './SearchBar';
import DurationIntervalComponent from './DurationIntervalComponent';
import ContextItem from './ContextItem';
import FavouriteTreeNodeComponent from './FavouriteTreeNodeComponent';
const TimeEntryModal = ({ showModal, setShowModal }) => {
// STATES
const [selected, setSelected] = useState('');
const [reportingTemplates, setReportingTemplates] = useState([]);
const [reportingContexts, setReportingContexts] = useState([]);
// USEREF
const modalRef = useRef();
// ANIMATION MODAL
const animation = useSpring({
config: {
duration: 250,
},
opacity: showModal ? 1 : 0,
transform: showModal ? `translateY(0%)` : `translateY(-100%)`,
});
// CLOSING MODAL FROM BACKGROUND
const closeModal = (e) => {
if (modalRef.current === e.target) {
setShowModal(false);
}
};
// CLOSING MODAL WITH ESCAPE BUTTON
const keyPress = useCallback(
(e) => {
if (e.key === 'Escape' && showModal) {
setShowModal(false);
console.log('I pressed');
}
},
[setShowModal, showModal]
);
useEffect(() => {
document.addEventListener('keydown', keyPress);
return () => document.removeEventListener('keydown', keyPress);
}, [keyPress]);
const variants = {
hidden: {
y: '-100vh',
},
visible: { y: 0, transition: { ease: 'easeInOut', duration: 1 } },
exit: {
y: '-100vh',
},
};
// DATA
const colorsData = [
'#86d8bda1',
'#eb472a83',
'#8fcfe3ad',
'#db8ddfb0',
'#e8e46fc0',
'#e1aa66b2',
];
const List = [
'3000',
'4000',
'5000',
'6000',
'7000',
'8000',
];
// FAVOURITES LIST ANIMATION
const [updatedList, updatePosition, updateOrder] =
usePositionReorder(List);
// FETCHES
useEffect(() => {
getTemplates();
}, []);
const url = 'http://localhost:8080/graphql';
const getTemplates = async () => {
try {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `query {
reportingTemplates {
name
_id
colorScheme
reportingContexts {
name
_id
}
}
}`,
}),
});
const data = await res.json();
console.log(data);
const dataWithColors = data.data.reportingTemplates.map((obj, i) => ({
...obj,
color: colorsData[i],
}));
setReportingTemplates(dataWithColors);
setSelected(dataWithColors[0].color);
setReportingContexts(dataWithColors[0].reportingContexts);
console.log('data', dataWithColors);
} catch (err) {
console.log('ERROR', err);
}
};
console.log('data state', reportingTemplates);
return (
<Background ref={modalRef} onClick={closeModal}>
<motion.div
variants={variants}
initial="hidden"
animate="visible"
exit="exit"
>
<ModalWrapper>
<ModalContent>
<Nav>
<h3>New Entry Tirsdag 2. Sep </h3>
</Nav>
<ModalColumns>
<ColumnOne>
<TemplatesContainer>
<AnimateSharedLayout>
<Templates>
{reportingTemplates.map((template, index) => (
<TemplateItem
key={template.name}
color={template.color}
title={template.name}
isSelected={selected === template.color}
onClick={() => {
setReportingContexts(
reportingTemplates[index].reportingContexts
);
setSelected(template.color);
}}
/>
))}
</Templates>
</AnimateSharedLayout>
</TemplatesContainer>
<InformationBlock>
<h4>Information</h4>
{reportingContexts.map((context) => (
<ContextItem
key={context.name}
text={context.name}
// isSelected={selected === context.color}
// onClick={() => setSelected(context.color)}
/>
))}
</InformationBlock>
<DurationIntervalComponent />
</ColumnOne>
<ColumnTwo>
<SearchBar />
<Recent>
<h4>Recent</h4>
</Recent>
<Favourites>
<h4>Favourites</h4>
<FavouriteTreeNodesList>
{updatedList.map((treeNode, index) => (
<FavouriteTreeNodeComponent
key={index}
index={index}
text={treeNode}
updateOrder={updateOrder}
updatePosition={updatePosition}
/>
))}
</FavouriteTreeNodesList>
</Favourites>
</ColumnTwo>
</ModalColumns>
</ModalContent>
<CloseModalButton
aria-label="Close Modal"
onClick={() => setShowModal((prev) => !prev)}
/>
<Footer>
<DeleteBlock>
<p> Delete </p>
</DeleteBlock>
<CancelNSaveBlock>
<Cancel>
<p> Cancel </p>
</Cancel>
<SaveNClose>
<p> Save and close </p>
</SaveNClose>
<SaveNAdd>
<p> Save and add another </p>
</SaveNAdd>
</CancelNSaveBlock>
</Footer>
</ModalWrapper>
</motion.div>
</Background>
);
};
// STYLES
const Background = styled.div`
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
position: fixed;
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
`;
const ModalWrapper = styled.div`
width: 80vw;
height: 80vh;
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.2);
background: #fff;
color: #000;
display: flex;
flex-direction: column;
align-items: stretch;
border-radius: 10px;
`;
const CloseModalButton = styled(MdClose)`
cursor: pointer;
position: absolute;
top: 20px;
right: 20px;
width: 32px;
height: 32px;
padding: 0;
z-index: 10;
`;
// MODAL CONTENT
const ModalContent = styled.div`
padding: 20px;
position: relative;
`;
// NAV
const Nav = styled.div``;
const ModalColumns = styled.div`
padding: 10px 0px;
display: flex;
`;
// COLUMN ONE
const ColumnOne = styled.div`
width: 50%;
padding: 20px;
`;
const TemplatesContainer = styled.div``;
const Templates = styled.ul`
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: initial;
justify-content: center;
`;
const InformationBlock = styled.div`
padding-bottom: 20px;
`;
// COLUMN TWO
const ColumnTwo = styled.div`
width: 50%;
padding: 20px;
`;
const Recent = styled.div`
background-color: #f6f6f9;
border: 1px solid #c5c5c5;
border-radius: 20px;
`;
const Favourites = styled.div`
list-style: none;
`;
const FavouriteTreeNodesList = styled.ul`
position: relative;
width: 100%;
`;
// FOOTER
const Footer = styled.div`
display: flex;
position: fixed;
padding: 0 20px;
bottom: 10px;
/* text-align: center; */
width: 100%;
`;
const DeleteBlock = styled.div`
color: #fb423a;
`;
const CancelNSaveBlock = styled.div`
display: flex;
margin-left: auto;
`;
// const [{name:"cancel", color func: ()=>sdasd }, "sada", "asdasd"]
const Cancel = styled.div`
color: #02b396;
margin-right: 40px;
`;
const SaveNClose = styled.div`
font-weight: bolder;
color: #02b396;
margin-right: 40px;
`;
const SaveNAdd = styled.div`
font-weight: bolder;
color: #02b396;
`;
export default TimeEntryModal;
After that the list FavouriteTreeNodeComponent is rendered in the code below:
import { useState } from 'react';
// styled components
import styled from 'styled-components';
// framer motion
import { motion } from 'framer-motion';
import { useMeasurePosition } from './ReorderingModalNodes/use-measure-position';
const FavouriteTreeNodeComponent = ({
text,
updateOrder,
updatePosition,
index,
}) => {
const [isdragged, setIsDragged] = useState(false);
const itemRef = useMeasurePosition((pos) => updatePosition(index, pos));
return (
<ListContainer>
<motion.div
style={{
zIndex: isdragged ? 2 : 1,
height: text.length * 10,
padding: "5px",
}}
dragConstraints={{
top: 0,
bottom: 0,
}}
dragElastic={1}
layout
ref={itemRef}
onDragStart={() => setIsDragged(true)}
onDragEnd={() => setIsDragged(false)}
animate={{
scale: isdragged ? 1.05 : 1,
}}
onViewportBoxUpdate={(_, delta) => {
isdragged && updateOrder(index, delta.y.translate);
}}
drag="y">{text}
</motion.div>
</ListContainer>
);
};
// STYLES
const ListContainer = styled.li`
height: auto;
gap: 9px 0px;
position: relative;
padding: 10px 0px;
list-style: none;
div {
background-color: white;
border-radius: 5px;
border: 1px solid #c5c5c5;
}
`;
export default FavouriteTreeNodeComponent;
The above code is connected to the file: useMeasurePosition same as the original project:
import { useEffect, useRef } from 'react';
export function useMeasurePosition(update) {
// We'll use a `ref` to access the DOM element that the `motion.li` produces.
// This will allow us to measure its height and position, which will be useful to
// decide when a dragging element should switch places with its siblings.
const ref = useRef(null);
// Update the measured position of the item so we can calculate when we should rearrange.
useEffect(() => {
update({
height: ref.current.offsetHeight,
top: ref.current.offsetTop,
});
});
return ref;
}
and to the file: usePositionReorder:
import { useState, useRef } from 'react';
import { clamp, distance } from 'popmotion';
import { arrayMoveImmutable as move} from 'array-move';
export function usePositionReorder(initialState) {
const [order, setOrder] = useState(initialState);
// We need to collect an array of height and position data for all of this component's
// `Item` children, so we can later us that in calculations to decide when a dragging
// `Item` should swap places with its siblings.
const positions = useRef([]).current;
const updatePosition = (i, offset) => (positions[i] = offset);
// Find the ideal index for a dragging item based on its position in the array, and its
// current drag offset. If it's different to its current index, we swap this item with that
// sibling.
const updateOrder = (i, dragOffset) => {
const targetIndex = findIndex(i, dragOffset, positions);
if (targetIndex !== i) setOrder(move(order, i, targetIndex));
};
return [order, updatePosition, updateOrder];
}
const buffer = 30;
export const findIndex = (i, yOffset, positions) => {
let target = i;
const { top, height } = positions[i];
const bottom = top + height;
// If moving down
if (yOffset > 0) {
const nextItem = positions[i + 1];
if (nextItem === undefined) return i;
const swapOffset =
distance(bottom, nextItem.top + nextItem.height / 2) + buffer;
if (yOffset > swapOffset) target = i + 1;
// If moving up
} else if (yOffset < 0) {
const prevItem = positions[i - 1];
if (prevItem === undefined) return i;
const prevBottom = prevItem.top + prevItem.height;
const swapOffset = distance(top, prevBottom - prevItem.height / 2) + buffer;
if (yOffset < -swapOffset) target = i - 1;
}
return clamp(0, positions.length, target);
};

Nothing was returned from render. This usually means a return statement is missing. Error

I can't get my head around why I'm getting this error. Does anyone know what is wrong with the code? The code worked when there was only the "class image" and the error started appearing after I added back "function App()".
App.js =>
import React, {useEffect, useState} from "react";
import "./App.css";
import Navbar from './components/Navbar/Navbar'
import "bootstrap/dist/css/bootstrap.min.css";
import UploadImages from "./components/image-upload.component";
import styled from "styled-components";
function App() {
const [url, setURL] = useState("");
const Button = styled.button`
background-color: black;
color: white;
font-size: 20px;
padding: 10px 60px;
border-radius: 5px;
margin: 10px 0px;
cursor: pointer;
`;
const [urls, setURLs] = useState([]);
useEffect(() => {
const urls = [
250940,
20622,
436625,
436444,
436509,
359245,
459090,
333933,
333916,
466350,
44831,
383010,
202660,
406317,
337349,
503448,
12617,
248662,
435805,
438545
].map(
(itemId) =>
`https://collectionapi.metmuseum.org/public/collection/v1/objects/${itemId}`
);
Promise.all(
urls.map((currUrl) =>
fetch(currUrl)
.then((response) => response.json())
.then((data) => data.primaryImage)
.catch((error) => console.log("There was a problem!", error))
)
).then((fetchedUrls) => setURLs(fetchedUrls));
}, []);
class image extends React.Component {
constructor(props) {
super(props);
this.onClickNext = this.onClickNext.bind(this)
this.onClickPrevious = this.onClickPrevious.bind(this)
const img0 = 'https://images.metmuseum.org/CRDImages/gr/original/DP146615.jpg'
const img1 = 'https://images.metmuseum.org/CRDImages/ad/original/DP253076.jpg'
const img2 = 'https://images.metmuseum.org/CRDImages/ep/original/DP146499.jpg'
const img3 = 'https://images.metmuseum.org/CRDImages/ep/original/EP1455.jpg'
const img4 = 'https://images.metmuseum.org/CRDImages/ep/original/DT2580.jpg'
const img5 = 'https://images.metmuseum.org/CRDImages/dp/original/DP821059.jpg'
const img6 = 'https://images.metmuseum.org/CRDImages/rl/original/DP295708.jpg'
const img7 = 'https://images.metmuseum.org/CRDImages/dp/original/DP805154.jpg'
this.state = {
index: 0,
imgList: [img0 ,img1, img2, img3, img4, img5, img6, img7]
}
}
// imgList: [urls[1], urls[2], urls[3], urls[4], urls[5], urls[6], urls[7], urls[8], urls[9], urls[10], urls[11], urls[12], urls[13], urls[14], urls[15], urls[16], urls[17], urls[18], urls[19], urls[20]]
onClickNext() {
if (this.state.index + 1 === this.state.imgList.length) {
this.setState({
index: 0
})
} else {
this.setState({
index: this.state.index + 1
})
}
}
onClickPrevious() {
if (this.state.index - 1 === -1) {
this.setState({
index: this.state.imgList.length -1
})
} else {
this.setState({
index: this.state.index -1
})
}
}
render() {
return (
<div>
<div className="productsContainer">
<div className="images">
<div className="images__item">
<img className="photo" src={this.state.imgList[this.state.index]}/>
</div>
<div className="images__item">
<img className="photo" src={this.state.imgList[this.state.index + 1]}/>
</div>
<div className="images__item">
<img className="photo" src={this.state.imgList[this.state.index + 2]}/>
</div>
</div>
</div>
<button onClick={this.onClickPrevious}>Previous</button>
<button onClick={this.onClickNext}>Next</button>
</div>
)
}
}
}
export default App;
Full error: App(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
You are not rendering anything in your <App>. You need to return your component. I renamed it from <Image> to <ImageCarousel>.
I also cleaned-up the modular math.
const { useEffect, useState } = React;
const images = [
'https://images.metmuseum.org/CRDImages/gr/original/DP146615.jpg',
'https://images.metmuseum.org/CRDImages/ad/original/DP253076.jpg',
'https://images.metmuseum.org/CRDImages/ep/original/DP146499.jpg',
'https://images.metmuseum.org/CRDImages/ep/original/EP1455.jpg',
'https://images.metmuseum.org/CRDImages/ep/original/DT2580.jpg',
'https://images.metmuseum.org/CRDImages/dp/original/DP821059.jpg',
'https://images.metmuseum.org/CRDImages/rl/original/DP295708.jpg',
'https://images.metmuseum.org/CRDImages/dp/original/DP805154.jpg'
];
class ImageCarousel extends React.Component {
constructor(props) {
super(props);
const { images } = props;
this.onClickNext = this.onClickNext.bind(this)
this.onClickPrevious = this.onClickPrevious.bind(this)
this.state = {
index: 0,
imgList: images
}
}
onClickNext() {
this.rotateIndex(1);
}
onClickPrevious() {
this.rotateIndex(-1);
}
rotateIndex(direction) {
const { index, imgList: { length }} = this.state;
this.setState({
index: this.calculateIndexOffset(index, direction, length)
});
}
calculateIndexOffset(start, offset, size) {
return (start + size + offset) % size;
}
indexOffset(offset = 0) {
const { index, imgList: { length }} = this.state;
return this.calculateIndexOffset(index, offset, length);
}
imageOffset(offset = 0) {
return this.state.imgList[this.indexOffset(offset)];
}
render() {
return (
<div className="imageCarousel">
<div className="productsContainer">
<div className="images">
<div className="images__item">
<img className="photo" src={this.imageOffset()}/>
</div>
<div className="images__item">
<img className="photo" src={this.imageOffset(1)}/>
</div>
<div className="images__item">
<img className="photo" src={this.imageOffset(2)}/>
</div>
</div>
</div>
<div className="buttons">
<button onClick={this.onClickPrevious}>Previous</button>
<button onClick={this.onClickNext}>Next</button>
</div>
</div>
)
}
}
const App = () => {
const [url, setURL] = useState("");
const [urls, setURLs] = useState([]);
useEffect(() => {
const urls = [
250940,
20622,
436625,
436444,
436509,
359245,
459090,
333933,
333916,
466350,
44831,
383010,
202660,
406317,
337349,
503448,
12617,
248662,
435805,
438545
].map(
(itemId) =>
`https://collectionapi.metmuseum.org/public/collection/v1/objects/${itemId}`
);
Promise.all(
urls.map((currUrl) =>
fetch(currUrl)
.then((response) => response.json())
.then((data) => data.primaryImage)
.catch((error) => console.log("There was a problem!", error))
)
).then((fetchedUrls) => setURLs(fetchedUrls));
}, []);
return <ImageCarousel images={images} />
}
ReactDOM.render(<App />, document.getElementById("react"));
.imageCarousel {
display: grid;
grid-template-columns: 1fr;
grid-row-gap: 0.5em;
}
.imageCarousel .productsContainer .images {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.images__item {
display: flex;
justify-content: center;
}
.images__item .photo {
max-height: 160px;
flex: 1;
}
.imageCarousel .buttons {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 1em;
justify-content: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

How to switch image on scroll in React (like on Apple website)?

I try to switch from an image to another one when scrolling with React but I don't know how to do this... maybe with React hook. I know it's possible with jQuery but I don't want to use it.
Example here: https://www.apple.com/iphone-12/, look at the "Five fresh finishes".
import React, {useState} from 'react';
import './BackgroundDiscover.css';
import logoOrdiW from "../../images/mathis_computer_white.jpg";
import logoOrdiB from "../../images/mathis_computer_black.jpg";
const BackgroundDiscover = () => {
const [imageSrc, SetSrc] = useState(logoOrdiW);
const switchBrackground = () => {
SetSrc(logoOrdiB);
}
return (
<>
<div className="container-discover">
<div className='container-logo-white'>
<img src={imageSrc} alt="logo ordinateur mathis blanc" onScroll={switchBrackground}/>
</div>
</div>
</>
);
};
export default BackgroundDiscover;
.container-discover {
display: flex;
flex-direction: column;
}
.container-logo-white img {
width: 100%;
}
.container-logo-black img {
width: 100%;
}
You don't need jQuery or something like this. You could subscribe to scroll event in useEffect() hook with addEventListener and check scrollTop of a scrolling container.
If you want to do the same effect as the Apple website has, you could add some magic with position: sticky, height in vh units and checking window.innerHeight.
Note that this code is simplified, not optimized, and only needed to understand the idea:
CodeSandbox
index.js:
import { useLayoutEffect, useState } from "react";
import { render } from "react-dom";
import classnames from "classnames";
import "./index.css";
const images = [0, 1, 2, 3, 4];
const App = () => {
const [visibleImagesMap, setVisibleImagesMap] = useState(
images.reduce((map, image) => {
map[image] = false;
return map;
}, {})
);
useLayoutEffect(() => {
const handleScroll = () => {
const scrollTop = document.documentElement.scrollTop;
const viewportHeight = window.innerHeight;
const newVisibleImagesMap = images.reduce((map, image) => {
map[image] = scrollTop >= image * viewportHeight;
return map;
}, {});
setVisibleImagesMap(newVisibleImagesMap);
};
window.addEventListener("scroll", handleScroll);
handleScroll();
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<div className="app">
<div className="sticky">
<div className="frame">
{images.map((image) => (
<div
className={classnames("image", `image_${image}`, {
image_visible: visibleImagesMap[image]
})}
key={image}
/>
))}
</div>
</div>
</div>
);
};
render(<App />, document.getElementById("root"));
index.css:
body {
margin: 0;
}
.app {
height: 500vh;
}
.sticky {
position: sticky;
top: 0;
height: 100vh;
}
.frame {
z-index: 1;
position: relative;
height: 100%;
width: 100%;
}
.image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
background-repeat: no-repeat;
background-size: cover;
}
.image_0 {
z-index: 0;
background-image: url("./images/0.jpeg");
}
.image_1 {
z-index: 1;
background-image: url("./images/1.jpeg");
}
.image_2 {
z-index: 2;
background-image: url("./images/2.jpeg");
}
.image_3 {
z-index: 3;
background-image: url("./images/3.jpeg");
}
.image_4 {
z-index: 4;
background-image: url("./images/4.jpeg");
}
.image_visible {
opacity: 1;
}
There are plenty of solutions for this but basically you can set in this way.
Wheel event listener turns either positive or negative value so you should create an algorithm if you want to impelement different designs.
import React, { useState } from "react";
export default function App() {
const [bgImage, handleImage] = useState(
"https://media.wired.com/photos/5e9f56f143e5800008514457/1:1/w_1277,h_1277,c_limit/Gear-Feature-Apple_new-iphone-se-white_04152020.jpg"
);
window.addEventListener("wheel", (e) => {
if (e.deltaY > 0) {
handleImage(
"https://media.wired.com/photos/5bcea2642eea7906bba84c67/master/w_2560%2Cc_limit/iphonexr.jpg"
);
} else {
handleImage(
"https://media.wired.com/photos/5e9f56f143e5800008514457/1:1/w_1277,h_1277,c_limit/Gear-Feature-Apple_new-iphone-se-white_04152020.jpg"
);
}
});
return (
<div className="App">
<img
style={{ width: "400px", height: "300px" }}
id="myimage"
src={bgImage}
alt=""
/>
</div>
);
}
And as a result you can use bgImage state in your image src.
This is a codesandbox example https://codesandbox.io/s/pensive-vaughan-cfc63?fontsize=14&hidenavigation=1&theme=dark
You can try useEffect and useRef.
useEffect is sideload functions, and useRef is for real dom referances.
import React, { useRef, useState, useEffect } from 'react';
import logoOrdiW from "../../images/mathis_computer_white.jpg";
import logoOrdiB from "../../images/mathis_computer_black.jpg";
import './BackgroundDiscover.css';
const BackgroundDiscover = () => {
const img = useRef();
const [src, setSrc] = useState(logoOrdiW);
const switchBrackground = () => {
setSrc(logoOrdiB);
}
useEffect(() => {
img.current.onscroll = switchBrackground;
}, []);
return (
<div className="container-discover">
<div className='container-logo-white'>
<img src={src} alt="logo ordinateur mathis blanc" ref={img} />
</div>
</div>
);
};
export default BackgroundDiscover;

How can I repeat a single column across each row?

Context:
I have a small piece of code that I wrote to visualise some binary numbers. It's a self contained HTML file that should work in the browser.
Output:
Question:
How can I show the "Input" block (.input-block div) in each row of this flexbox layout?
Is this achievable using Flexbox?
.container {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-items: flex-start;
height: 80vh;
}
.block {
padding: 15px;
flex: 0 1 150px;
}
.input-block {
border: 1px solid black;
}
.block:hover {
background-color: rgb(231, 231, 231);
}
.row {
padding: 10px;
text-align: right;
color: rgb(61, 61, 61);
letter-spacing: 1.4px;
}
.row:first-child {
font-weight: bold;
color: rgb(83, 83, 83);
background-color: burlywood;
}
.row:nth-child(even) {
background-color: rgb(255, 241, 219);
}
.row-head:nth-child(even) {
background-color: rgb(205, 255, 200);
}
<body>
<div id="root"></div>
</body>
<script type="module">
import { html, render } from 'https://unpkg.com/lit-html?module';
function main() {
const arr = new Array(32).fill(0).map((x, i) => i);
const elements = arr.map(x => getBinaryAssociates(x));
let state = { elements }
display(state);
}
/**
* #param { { elements: ReturnType<typeof getBinaryAssociates>[] } } state
*/
function display(state) {
const root = document.getElementById("root");
/** #type {(_: typeof state) => any} */
const tmpl = (state) => html`<div class="container">
${getHeaderBlockTmpl()}
${state.elements.map(getItemBlockTmpl)}
<div>`;
render(tmpl(state), root);
}
function getHeaderBlockTmpl() {
return html`<div class="block input-block">
<div class="row row-head">Input</div>
${FUNCTIONS.map(x => html`<div class="row row-head">${x.toString()}</div>`)}
</div>`;
}
function getItemBlockTmpl(item) {
return html`<div class="block">
<div class="row">
${numToString(item.input)} ➡ ${item.binary}
</div>
${item.outputs.map(x => html`<div class="row">
${x.value} ➡ ${x.binary}
</div>`)}
</div>`;
}
const FUNCTIONS = [
x => x + 1,
x => x - 1,
x => x & (x - 1),
x => ~x + 1,
x => -(x + 1),
x => ~x,
];
function numToString(n) {
return n.toString();
}
function getBinaryAssociates(number) {
const binary = (x) => (x >>> 0).toString(2).slice(-6).padStart(6, '0');
let res = FUNCTIONS
.map(x => ({
function: x,
value: numToString(x(number)),
binary: binary(x(number)),
}));
let result = res.reduce((acc, n) => {
acc.outputs.push(n);
return acc;
}, {
input: number,
binary: binary(number),
/** #type {typeof res} */
outputs: [],
});
return result;
}
function init() {
document.addEventListener("DOMContentLoaded", () => {
main();
});
}
init();
</script>

Categories

Resources