How to center rotation axis properly in react-transition-group animation - javascript

I have problem with setting rotation axis in a animation of menu buttons in react webpage.
Im using react 17.0.2 and react-transition-group 4.4.2.
The concept of my menu is to rotate the button after it has been clicked.
If other button was clicked before, it shoud rotate back to its original position, and the new button should rotate - to show it's active.
I have the problem with making animation axis of rotation perfectlly centered:
[Menu buttons before rotation] [1]
: https://i.stack.imgur.com/7nVnv.jpg
[After rotation menu button is moved to the right] [2]
: https://i.stack.imgur.com/yB28J.jpg
As you can see in the image [2] middle menu button moved to the right and is not aligned with the rest of the buttons, I want it to be perfectlly aligned with all the buttons.
My NavigationItems.js describes the buttons:
import React, { useState } from "react";
import { TransitionGroup } from "react-transition-group";
import classes from "./NavigationItems.module.scss";
import NavigationItem from "./NavigationItem/NavigationItem"
const data = [
{in:false, id:0, desc:"About me",href:"/photo-gallery/info"},
{in:false, id:1, desc:"Photo gallery",href:"/photo-gallery/photos"},
{in:false, id:2, desc:"Some tests",href:"/photo-gallery/education"}
];
const NavigationItems = () => {
const [allButtons, setAllButtons] = useState(data);
const [prevButton, setPrevButton] = useState({
in:false, id:-1, desc:"",href:""
});
const allButtonsDeepUpdate = (idx, obj, updatePrevButton) => {
const allButtonsCpy = [];
for(let i=0;i<allButtons.length;i++) {
if(i===idx) {
allButtonsCpy.push(Object.assign({},obj));
} else if (updatePrevButton && i===prevButton.id) {
allButtonsCpy.push(Object.assign({},prevButton));
} else {
allButtonsCpy.push(Object.assign({},allButtons[i]));
};
};
setAllButtons(allButtonsCpy);
};
const enterAnimation = (idx) => {
if(allButtons[idx].id !== prevButton.id) {
const newButton = {...allButtons[idx], ...{in:true}};
if (prevButton.id !== -1)
setPrevButton({...prevButton,...{in:false}});
console.log("newButton:",newButton) ;
console.log("prevButton:",prevButton);
allButtonsDeepUpdate(idx, newButton, prevButton.id>=0 ? true : false)
setPrevButton(Object.assign({},allButtons[idx]));
}
};
return (
<div>
<TransitionGroup component="ul" className={classes.NavigationItems}>
{allButtons.map((button) => (
<NavigationItem
starter={button.in}
pkey={button.id}
timeout={1000}
click={enterAnimation.bind(this,button.id)}
link={button.href}
>
{button.desc}
</NavigationItem>
))}
</TransitionGroup>
</div>
);
};
export default NavigationItems;
The coresponding scss classes (NavigationItems.module.scss) of NavigationItems.js look like this:
#import '../../../sass/abstracts/variables.scss';
.NavigationItems {
margin: 0;
padding: 0;
list-style: none;
display: block;
//flex-flow: column;
//flex-direction: column;
//justify-content: center;
height: 100%;
}
#media (min-width: $min-width-small-res) {
.NavigationItems {
flex-flow: row;
flex-direction: row;
justify-content: space-evenly;
}
}
Note that when I switch styles to flexbox the rotation axis is also not centered properly.
The NavigationItem.js I am animating looks like this (I am using react-router-dom 6 but it can be also used with 5):
import React from 'react';
import { NavLink } from 'react-router-dom';
import { CSSTransition } from 'react-transition-group';
import classes from './NavigationItem.module.scss';
const NavigationItem = (props) => {
const nodeRef = React.createRef();
return (
<CSSTransition
key={props.pkey}
nodeRef={nodeRef}
in={props.starter}
classNames={{
enter: classes.NavigationItemEnter,
enterActive: classes.NavigationItemEnterActive,
enterDone: classes.NavigationItemEnterDone,
exit: classes.NavigationItemExit,
exitActive: classes.NavigationItemExitActive,
exitDone: classes.NavigationItemExitDone,
}}
timeout={props.timeout}
>
<li ref={nodeRef} className={classes.NavigationItem} onClick={props.click}>
<NavLink
// activeClassName={classes.active} // react-router-dom v.5
//className={({isActive}) => isActive ? classes.active : ''} // v.6
to={props.link}
exact={props.exact}
>
{props.children}
</NavLink>
</li>
</CSSTransition>
);
}
export default NavigationItem;
The coresponding classes (NavigationItem.module.scss) look like this:
#import '../../../../sass/abstracts/variables.scss';
.NavigationItem {
box-sizing: border-box;
display: inline-block;
width: 100%;
//backface-visibility: hidden;
transform-origin: center center 0;
&Enter {
transform: rotate(0deg);
}
&EnterActive {
transform: rotate(180deg);
transition: transform 500ms linear
}
&EnterDone {
transform: rotate(180deg);
}
&Exit {
transform: rotate(180deg);
}
&ExitActive {
transform: rotate(0deg);
transition: transform 500ms linear;
}
&ExitDone {
transform: rotate(0deg);
}
& a {
border-radius: 15%;
margin-right: 15px;
background-color: $color-secondary-light;
color: $color-primary;
text-decoration: none;
width: 100%;
//box-sizing: border-box;
display: block;
}
& a:hover {
color: $color-alert;
}
& a:active,
& a.active {
color: $color-quaduprary;
background-color: $color-secondary-dark;
}
}
#media (min-width: $min-width-small-res) {
.NavigationItem {
margin: 0;
left: 0;
display: flex;
height: 100%;
width: auto;
align-items: center;
& a {
color: $color-tertiary;
height: 100%;
padding: 10px 10px;
}
& a:active,
& a.active {
background-color: $color-secondary-dark;
color: $color-quaduprary;
border-color: $color-alert;
}
}
}
The last files are package.json and variables.scss:
$color-primary: #845EC2;
$color-secondary-light: #FF6F91;
$color-secondary-dark: #D65DB1;
$color-tertiary: #009b1a;
$color-quaduprary: #009b1a;
$color-alert: #FFC75F;
//sizes
$menu-button-width: 9.5rem;
//text
$text-large: 1.5rem;
$text-small: 1rem;
$text-supersmall: 0.8rem;
//round pixel
$round-small: 3px;
$round-medium: 10rem;
$round-large: 50%;
//CV header
$photoHeight: 80%;
$small-res-photoHeight: 90%;
//Media Queries
$max-width-intermediate-res: 1050px;
$max-width-medium-res: 730px;
$max-width-small-res: 564px;
$min-width-small-res: 565px;
//csv filters:
$filter-main: invert(93%) sepia(90%) saturate(2%) hue-rotate(357deg) brightness(108%) contrast(100%);
json:
{
"name": "photo-gallery",
"version": "0.1.0",
"private": true,
"dependencies": {
"#testing-library/jest-dom": "^5.16.2",
"#testing-library/react": "^12.1.4",
"#testing-library/user-event": "^13.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
"react-transition-group": "^4.4.2",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"sass": "^1.49.9"
}
}
As you can see I tried to center rotation axis with "transform-origin: center center 0;" (also tried "transform-origin: center center" and "transform-origin: center) but with no luck.
I think I'm missing something here, any help, suggestions would be appreciated!
I also have two warnings with js code above regarding keys in rendering list and deprecated findDOMNode in StrictMode but that is the material for 2 next posts.
Thanks in advance for your answers!!!

As it was mentioned in first comment, the value of an anchor was set to 15px which made it rotate out of center. In order to make it work I either have to remove margin-right, or add margin-left with the same value.
Thanks a lot A Howorth!

Related

How come I keep getting this error ERROR Router' is not defined?

I keep getting this error in my react app:
ERROR
[eslint]
src\App.js
Line 14:6: 'Router' is not defined react/jsx-no-undef
Line 15:8: 'Navbar' is not defined react/jsx-no-undef
Line 16:8: 'Routes' is not defined react/jsx-no-undef
Line 17:10: 'Route' is not defined react/jsx-no-undef
Line 18:10: 'Route' is not defined react/jsx-no-undef
Line 19:10: 'Route' is not defined react/jsx-no-undef
Line 20:10: 'Route' is not defined react/jsx-no-undef
Line 21:10: 'Route' is not defined react/jsx-no-undef
Line 22:10: 'Route' is not defined react/jsx-no-undef
Line 23:10: 'Route' is not defined react/jsx-no-undef
Search for the keywords to learn more about each error.
This is my NavbarElements.js code:
import { FaBars } from 'react-icons/fa';
import { NavLink as Link } from 'react-router-dom';
import styled from 'styled-components';
export const Nav = styled.nav`
background: #63D471;
height: 85px;
display: flex;
justify-content: space-between;
padding: 0.2rem calc((100vw - 1000px) / 2);
z-index: 12;
/* Third Nav */
/* justify-content: flex-start; */
`;
export const NavLink = styled(Link)`
color: #808080;
display: flex;
align-items: center;
text-decoration: none;
padding: 0 1rem;
height: 100%;
cursor: pointer;
&.active {
color: #000000;
}
`;
export const Bars = styled(FaBars)`
display: none;
color: #808080;
#media screen and (max-width: 768px) {
display: block;
position: absolute;
top: 0;
right: 0;
transform: translate(-100%, 75%);
font-size: 1.8rem;
cursor: pointer;
}
`;
export const NavMenu = styled.div`
display: flex;
align-items: center;
margin-right: -24px;
/* Second Nav */
/* margin-right: 24px; */
/* Third Nav */
/* width: 100vw;
white-space: nowrap; */
#media screen and (max-width: 768px) {
display: none;
}
`;
export const NavBtn = styled.nav`
display: flex;
align-items: center;
margin-right: 24px;
/* Third Nav */
/* justify-content: flex-end;
width: 100vw; */
#media screen and (max-width: 768px) {
display: none;
}
`;
export const NavBtnLink = styled(Link)`
border-radius: 4px;
background: #808080;
padding: 10px 22px;
color: #000000;
outline: none;
border: none;
cursor: pointer;
transition: all 0.2s ease-in-out;
text-decoration: none;
/* Second Nav */
margin-left: 24px;
&:hover {
transition: all 0.2s ease-in-out;
background: #fff;
color: #808080;
}
`;
This is the code in my app.js file:
import React from "react";
import "./index.css";
import "./components/Navbar";
import Home from "./pages";
import About from "./pages/about";
import Awards from "./pages/awards";
import Contact from "./pages/contact";
import Education from "./pages/education";
import Projects from "./pages/projects";
import Skills from "./pages/skills";
function App() {
return (
<Router>
<Navbar />
<Routes>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
<Route path='/education' component={Education} />
<Route path='/skills' component={Skills} />
<Route path='/projects' component={Projects} />
<Route path='/awards' component={Awards} />
<Route path='/contact' component={Contact} />
</Routes>
</Router>
);
}
export default App;
and finally my package.json file to show my dependencies:
{
"name": "react-feedback-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"#testing-library/jest-dom": "^5.16.5",
"#testing-library/react": "^13.4.0",
"#testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.1",
"react-scripts": "5.0.1",
"styled-components": "^5.3.6",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Can someone help me figure out why I got these error codes please?
Missing from your project's dependencies is the react-icons package. It needs to be added to the project.
Run: npm i -s react-icons to add it to the project and save the package.json file.
The is no imported Navbar component.
import "./components/Navbar";
should be something like
import Navbar from "./components/Navbar";
Just make sure that a Navbar component is actually exported from that location.
The Router, Routes, and Route components also need to be imported.
import {
BrowserRouter as Router,
Routes,
Route,
} from 'react-router-dom';
Additionally, you are using react-router-dom#6 and the component APIs changed significantly from previous versions. The Route component renders its content on a single element prop taking a React.ReactNode, a.k.a. JSX.
Example:
function App() {
return (
<Router>
<Navbar />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/about' element={<About />} />
<Route path='/education' element={<Education />} />
<Route path='/skills' element={<Skills />} />
<Route path='/projects' element={<Projects />} />
<Route path='/awards' element={<Awards />} />
<Route path='/contact' element={<Contact />} />
</Routes>
</Router>
);
}

React Slick slider, current active slide overlapped by next slide

I'm currently using Slick in order to make a carousel.
I'm having two issues right now, let's start with the first one.
1)
I'm currently using a slider in which i want to show 3 slides: the current image (Spyro), the previous one (Crash) and the next one (Tekken).
As you see, while the current slide correctly overlaps the previous one (Spyro > Crash), the next one overlaps the current slide (Tekken > Spyro).
Of course i want the current slide to be on top of both of them... How can i fix this?
I'm attacching the code below.
App.js
import "./App.css";
import { useEffect, useState } from "react";
import Slider from "react-slick";
import SliderData from "./SliderData";
import { AiOutlineArrowLeft, AiOutlineArrowRight } from "react-icons/ai";
function useWindowSize() {
const [size, setSize] = useState([window.innerHeight, window.innerWidth]);
useEffect(() => {
const handleResize = () => setSize([window.innerHeight, window.innerWidth]);
window.addEventListener("resize", handleResize);
}, [])
return size;
}
const array = SliderData.map((x) => {
return x.image;
})
console.log(array);
function App() {
const NextArrow = ({ onClick }) => {
return (
<div className="arrow next" onClick={onClick}>
<AiOutlineArrowRight />
</div>
);
};
const PrevArrow = ({ onClick }) => {
return (
<div className="arrow prev" onClick={onClick}>
<AiOutlineArrowLeft />
</div>
);
};
const [imageIndex, setImageIndex] = useState(0);
const [height, width] = useWindowSize();
const settings = {
className: "center",
infinite: true,
lazyLoad: true,
speed: 300,
slidesToShow: width > 1000 ? 3: 1,
centerMode: true,
centerPadding: "60px",
nextArrow: <NextArrow />,
prevArrow: <PrevArrow />,
beforeChange: (current, next) => {
console.log(current);
setImageIndex(next);
}
};
return (
<div className="App">
<Slider {...settings}>
{array.map((img, idx) => (
<div className={idx === imageIndex ? "slide activeSlide" : "slide"}>
<img src={img} alt={img} />
</div>
))}
</Slider>
</div>
);
}
export default App;
App.css
#import "~slick-carousel/slick/slick.css";
#import "~slick-carousel/slick/slick-theme.css";
.App {
width: 100%;
margin: 10rem auto;
height: 570px;
}
.slide img {
width: 35rem;
align-items: center;
margin: 0 auto;
z-index: 1;
}
.slide {
transform: scale(0.8);
transition: transform 300ms;
opacity: 0.5;
z-index: -1;
}
.activeSlide {
transform: scale(1.1);
align-items: center;
opacity: 1;
}
.arrow {
background-color: #fff;
position: absolute;
cursor: pointer;
z-index: 10;
}
.arrow svg {
transition: color 300ms;
}
.arrow svg:hover {
color: #68edff;
}
.next {
right: 3%;
top: 50%;
}
.prev {
left: 3%;
top: 50%;
}
SliderData.js
const SliderData = [
{
image:
"https://www.spaziogames.it/wp-content/uploads/2020/06/Crash-4-Pirate_06-29-20.jpg"
},
{
image:
"https://d2skuhm0vrry40.cloudfront.net/2018/articles/2018-07-18-14-24/news-videogiochi-spyro-reignited-trilogy-video-di-gameplay-livello-colossus-1531920251281.jpg/EG11/thumbnail/750x422/format/jpg/quality/60"
},
{
image: "https://i.ytimg.com/vi/OUh82pOFGDU/maxresdefault.jpg"
},
{
image: "https://www.psu.com/wp/wp-content/uploads/2020/07/MetalGearSolidRemake-1024x576.jpg"
}
];
export default SliderData;
2)
As you see, the active slide is not perfectly centered. Since i suck in CSS i did not use the display: flex; command.
What do you suggest? how can i fix this?
Thank you all.
You need to apply a position element to .slide for the z-index to work properly.
Note: z-index only works on positioned elements (position: absolute, position: relative, position: fixed, or position: sticky) and flex items (elements that are direct children of display:flex elements).
You can read more on z-index here
This is my answer for this issue. Basically you should set position and z-index for every item and set higher z-index for current active item
.App .slick-slide {
position: relative;
z-index: 1;
/* your choice, but make sure z-index of active slide is higher than this value */
}
.App .slick-slide.slick-current {
z-index: 10;
}

No pics or api while generating Nuxt static page

I'm new to vue and Nuxt and I have a task to deploy a static page with youtube api and search feature
So Everytime I try to generate a static page using nuxt it comes without my pics or youtube api
index.vue
<template>
<div class="iframe">
<input class="textR" type="text" v-model="searchInput">
<button class="button" style="vertical-align:middle" #click="search(searchInput)" ><span> Search</span></button>
<div v-if="videos!=null" id="iframeContainer" style="display: inline-table;">
<iframe class="overall" v-for="item in videos" :key="item.id.videoId" width="640" height="480"
:src="'https://www.youtube.com/embed/'+item.id.videoId"
frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
</div>
</template>
<script>
export default {
created() {
let axios = require('axios');
let params = {
part: 'snippet',
key: 'myApiCode',
q: 'كايروكى',
maxResults: 5,
type: 'video'
};
var parent = this;
axios.get('https://www.googleapis.com/youtube/v3/search', { params })
.then(response => {
parent.videos = response.data.items }
)
.catch(error => console.error(error));
},
mounted(){
setTimeout(function(){ let count = document.getElementById("iframeContainer").children;
if (count.length%2 != 0) {count[count.length -1].style.cssText = "margin: auto; display: table-cell;"};
},1000)
},
data() {
return {
videos: null,
loading: true,
searchInput: ""
}
},
methods: {
search(keyword){
let axios = require('axios');
let params = {
part: 'snippet',
key: 'AIzaSyBqJ9T8tven3Ge0_hNDWKjJ3lqOoL5N90s',
q: keyword,
maxResults: 5,
type: 'video'
};
var parent = this;
axios.get('https://www.googleapis.com/youtube/v3/search', { params })
.then(response => {
parent.videos = response.data.items }
)
.catch(error => console.error(error));
},
}
}
</script>
<style>
body {background-color: black;}
input[type=text] {
background-color: white;
background-image: url('../components/searchicon.png');
background-position: -1px 0px;
background-repeat: no-repeat;
padding-left: 30px;
background-size: 14%;
color : rgb(0, 0, 0);
}
.overall{
padding: 23px 20px;
background-color: #272727;
margin: 40px 50px;
border: 1px red solid;
}
.iframe {
padding: 23px 20px;
margin: 40px 100px;
}
.button {
display: inline-block;
border-radius: 4px;
background-color: #f4511e;
border: none;
color: #FFFFFF;
text-align: center;
font-size: 28px;
padding: 20px;
width: 200px;
transition: all 0.5s;
cursor: pointer;
margin: 5px;
}
.textR{
padding: 5px 5px;
margin: 20px 50px;
}
.button span {
cursor: pointer;
display: inline-block;
position: relative;
transition: 0.5s;
}
.button span:after {
content: '\00bb';
position: absolute;
opacity: 0;
top: 0;
right: -20px;
transition: 0.5s;
}
.button:hover span {
padding-right: 25px;
}
.button:hover span:after {
opacity: 1;
right: 0;
}
</style>
nuxt.config.js
import colors from 'vuetify/es5/util/colors'
// const routerBase = process.env.DEPLOY_ENV === 'master' ? {
// router: {
// base: '/youtube-task/'
// }
// } : {}
export default {
// ...routerBase,
mode: 'universal',
/*
** Headers of the page
*/
head: {
titleTemplate: '%s - ' + process.env.npm_package_name,
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// router: {
// base: '/youtube-task/'
// },
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
],
/*
** Plugins to load before mounting the App
*/
plugins: [
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
'#nuxtjs/vuetify',
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://axios.nuxtjs.org/usage
'#nuxtjs/axios',
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {
},
/*
** vuetify module configuration
** https://github.com/nuxt-community/vuetify-module
*/
vuetify: {
customVariables: ['~/assets/variables.scss'],
theme: {
dark: true,
themes: {
dark: {
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3
}
}
}
},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend (config, ctx) {
}
}
}
package.json
{
"name": "youtube-task",
"version": "1.0.0",
"description": "My flawless Nuxt.js project",
"author": "MostafaDesoky",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"build:gh-pages": "cross-env DEPLOY_ENV=master nuxt build",
"generate:gh-pages": "cross-env DEPLOY_ENV=master nuxt generate",
"deploy": "push-dir --dir=dist --branch=master --cleanup"
},
"dependencies": {
"nuxt": "^2.0.0",
"#nuxtjs/axios": "^5.3.6"
},
"devDependencies": {
"#nuxtjs/vuetify": "^1.0.0",
"cross-env": "^7.0.2",
"push-dir": "^0.4.1"
}
}
Anyhelp would be great I tried alot of methods but nothing
I tried to make netlify generate the dist for me and it was the same with no pic nor youtube api
Edit #1 : When I run it on my localhost it works perfectly through npm run dev,
the problem when I try to generate the dist folder and upload it to anywhere
Have you tried to enable spa mode before generating static website ?
mode: 'spa',
And why don't you use the axios helper directly instead of requiring it ?
this.$axios.get('https://www.googleapis.com/youtube/v3/search', { params })
For your background images you have to use this syntax :
background-image: url('~folder/nameoffile.ext');

Adding and removing active class from elements

I'm trying to add and remove active class when I click on different panels that triggers a transition, so if I click on different panels it works, as in it triggers the transition and then it ends it when other panel gets clicked, but if I want to click on a panel that was already opened and closed it won't trigger it again on the first click adn that's not good UX.
I'm writing it in React and I am a beginner so maybe I'm not doing something right.
You can see the code below, I hope I gave all the right information.
componentDidMount() {
ReactDom.findDOMNode(this).addEventListener("transitionend", (e) => {
if (
e.propertyName.includes("flex") &&
e.target.classList.contains("open")
) {
e.target.classList.add("open-active");
}
});
ReactDom.findDOMNode(this).addEventListener("click", (e) => {
const elems = document.querySelector(".open-active");
if (elems !== null) {
elems.classList.remove("open-active", "open", "opac");
}
e.target.className = "open-active";
console.log(e);
});
}
render() {
const { index, top, bottom, image, open, onClick } = this.props;
const style = {
backgroundImage: `url(${image})`,
};
const openValue = open ? "open opac" : "";
return (
<div
className={`panel ${openValue}`}
style={style}
onClick={() => {
onClick(index);
}}
>
</div>
And the CSS
.panel > * {
margin: 0;
width: 100%;
transition: transform 0.5s;
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
.panel > *:first-child {
transform: translateY(-100%);
}
.panel.open-active > *:first-child {
transform: translateY(0);
}
.panel > *:last-child {
transform: translateY(100%);
}
.panel.open-active > *:last-child {
transform: translateY(0);
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
font-size: 16px;
flex: 5;
}
Hi you can follow this example:
import React, {useState} from "react";
import './styles/style.css'
export default function ShowHideExample() {
const [cssClass, setCssClass] = useState('hide');
return (
<div className="App">
<h2>Show or Hide div</h2>
<button onClick={() => {
(cssClass === 'hide')? setCssClass('show') : setCssClass('hide');
}}>Click me to show or hide the div
</button>
<div className={cssClass}>
<h1>This is dynamically shown</h1>
</div>
</div>
);
}
Here is the style.css file
.show{
display: block;
background: dodgerblue;
padding:20px;
}
.hide{
display: none;
}

How to display images without cropping using gatsby-image?

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' }}
/>

Categories