I'm working on a project in Next.js (React) for the first time. It's a static site (only frontend).
Somehow I run into lots of problems related to the CSS transitions and I can't catch what am I missing.
When using some ready components, for example Modal from react-bootstrap, the transition seems to be working on Modal show, but not on hide. Transitions from Accordion, from the same module, work fine, but didn't work from other Accordion component I tried before that.
I will put some code here, but I have a filling, that there is some fundamental css/js related topic that I don't understand fully, but my searches didn't bring me the answer yet.
Here is the sample of the component, the related CSS is the default bootstrap-css (for the sake of simplicity):
Nav.tsx
import Link from 'next/link'
import navStyles from '../styles/Nav.module.css'
import homeStyles from "../styles/Home.module.css"
import {useState} from "react";
import Modal from "react-bootstrap/cjs/Modal"
import Button from "react-bootstrap/cjs/Button";
const MyVerticallyCenteredModal = (props) => {
return (
<Modal
{...props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header>
<Button onClick={props.onHide}>X</Button>
</Modal.Header>
<Modal.Body>
<p style={{color: "white"}}>Lorem ipsum dolor sit amet, consectetur adipisicing
elit. Aliquid at consequuntur cum ducimus enim fuga hic laudantium magni
maiores, molestias nesciunt nihil nisi nostrum pariatur perspiciatis rem rerum
suscipit, unde?</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={props.onHide}>Close</Button>
</Modal.Footer>
</Modal>
);
}
const Nav = () => {
const [modalShow, setModalShow] = useState(false);
const NavComp = () => {
return (
<>
<div className={navStyles.wrapper}>
<div className={`${homeStyles.home} ${homeStyles.mobile}`}>
<div className={navStyles.footer}>
<button
className={`${navStyles.footerBtn}`}
onClick={() => setModalShow(true)}
style={{borderLeft: "1px rgba(70,70,70,0.5) solid"}}
>
Contact
</button>
<MyVerticallyCenteredModal
show={modalShow}
onHide={() => setModalShow(false)}
/>
</div>
</div>
</div>
</>
)
}
return (
<nav className={navStyles.nav}>
<div className={navStyles.container}>
<div className={navStyles.logo}>
<Link href='/'>
<div>
<p className={navStyles.kaveem}>KAVEEM</p>
</div>
</Link>
</div>
<NavComp/>
</div>
</nav>
);
};
export default Nav;
Related
Hello everyone i have a question according to my case, i have
index.js code bellow :
export default function Home({ productList }) {
return (
<div className={styles.container}>
<Head>
<title>OlResto UI Design With Next JS</title>
<meta name="description" content="This is stunning website build using next js" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Featured />
<ProductList productList={ productList } />
</div>
);
};
export const getServerSideProps = async () => {
const res = await axios.get("http://localhost:3000/api/products");
return {
props: {
productList: res.data
}
}
};
the second one i have file call ProductList.jsx
code below:
const ProductList = ({ productList }) => {
return (
<div className={styles.container}>
<h1 className={styles.title}>THE BEST PIZZA IN TOWN</h1>
<p className={styles.desc}>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint, nemo at, labore porro reiciendis rem ad tempora blanditiis adipisci a eum animi obcaecati, dignissimos non perspiciatis natus corrupti consectetur libero.</p>
<div className={styles.wrapper}>
{productList.map(product => (
<ProductCard key={product._id} product={product} />
))}
</div>
</div>
);
};
export default ProductList;
Those files produce an error like this :
TypeError: productList.map is not a function
<div className={styles.wrapper}>
{productList.map(product => (
^
<ProductCard key={product._id} product={product} />
))}
</div>
Sorry if my question messed up, hope you guys can help my problem, thank you in advance
I've created an accordion with TailwindCSS and Alpine.js which works fine except that I also want to change the icon in the button that expands the content when it's clicked.
This is what I have:
<div x-data="{selected:null,open:true}">
<dl class="faqs mx-auto max-w-2xl">
<dt>
<span class="faq-q">Question</span>
<button
type="button"
class="faq-toggle"
#click="selected !== 1 ? selected = 1 : selected = null, open = open"
:class="{ 'faq-open': open, 'faq-close': !(open) }"
>
<span>+</span>
<span class="hidden">-</span>
</button>
</dt>
<dd
class="faq-a overflow-hidden transition-all max-h-0 duration-700"
style="" x-ref="container1" x-bind:style="selected == 1 ? 'max-height: ' + $refs.container1.scrollHeight + 'px' : ''"
>
<div class="inner">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Iure rerum in tempore sit ducimus doloribus quod commodi eligendi ipsam porro non fugiat nisi eaque delectus harum aspernatur recusandae incidunt quasi.
</div>
</dd>
</dl>
</div>
and a link to a CodePen.
What I want to do is toggle the class of the button from faq-open to faq-close when the button is clicked. Although I may actually need to toggle a class on the parent dt too.
At the moment, the accordion expands when you click on the button, but the class doesn't change.
The problem is on this line
#click="selected !== 1 ? selected = 1 : selected = null, open = open"
You never change value of open, it is always the value at initialization, and that is open: true.
You need to switch it:
#click="selected !== 1 ? selected = 1 : selected = null, open = !open"
By the way, you don't need extra variable selected to control the hidden text, just one open variable is enough. Something like this:
<div x-data="{open: true}">
<dl class="faqs mx-auto max-w-2xl">
<dt>
<span class="faq-q">Question</span>
<button
type="button"
class="faq-toggle"
#click="open = !open"
:class="open ? 'faq-open' : 'faq-close'"
>
<span :class="open ? '' : 'hidden'">+</span>
<span :class="open ? 'hidden' : ''">-</span>
</button>
</dt>
<dd
class="faq-a overflow-hidden transition-all max-h-0 duration-700"
style="" x-ref="container1" x-bind:style="open ? 'max-height: ' + $refs.container1.scrollHeight + 'px' : ''"
>
<div class="inner">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Iure rerum in tempore sit ducimus doloribus quod commodi eligendi ipsam porro non fugiat nisi eaque delectus harum aspernatur recusandae incidunt quasi.
</div>
</dd>
</dl>
import 'twin.macro';
import React, { useRef, useState } from 'react';
import { BiPlus, BiMinus } from 'react-icons/bi';
const Accordion = ({ title, children }) => {
const [active, setActive] = useState(false);
const [height, setHeight] = useState('0px');
const contentSpace = useRef(null);
function toggleAccordion() {
setActive(!active);
setHeight(active ? '0px' : `${contentSpace.current?.scrollHeight}px`);
}
return (
<div tw="flex flex-col outline-none" role="button" tabIndex={0}>
<div
tw="flex flex-row items-center justify-between py-4 border-b border-t border-black "
onClick={toggleAccordion}>
<h1 tw="cursor-pointer text-base font-bold">{title}</h1>
{active ? <BiMinus /> : <BiPlus />}
</div>
<div
ref={contentSpace}
style={{ maxHeight: `${height}` }}
tw="overflow-auto overflow-y-hidden duration-700 ease-in-out">
<p tw="my-4">{children}</p>
</div>
</div>
);
};
export default Accordion;
I have 2 Material-ui Accordions which I want to expand to their full height in the following way:
Both of them collapsed.
One collapsed (take the maximum height possible)
Both expanded (each take 50% of the full height)
Can I achieve this with the following code simply with CSS? (thought it should be possible with flex-grow and flex-direction:column but I can't get it to work.
https://codesandbox.io/s/upbeat-tesla-uchsb?file=/accordionFullHeight
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Accordion from '#material-ui/core/Accordion';
import AccordionSummary from '#material-ui/core/AccordionSummary';
import AccordionDetails from '#material-ui/core/AccordionDetails';
import Typography from '#material-ui/core/Typography';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
background: 'green',
height: '90vh'
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular,
},
}));
export default function SimpleAccordion() {
const classes = useStyles();
return (
<div className={classes.root}>
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography className={classes.heading}>Accordion 1</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex,
sit amet blandit leo lobortis eget.
</Typography>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel2a-content"
id="panel2a-header"
>
<Typography className={classes.heading}>Accordion 2</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex,
sit amet blandit leo lobortis eget.
</Typography>
</AccordionDetails>
</Accordion>
</div>
);
}
Solved it using a controlled Accordion that uses flex-grow:1 only when its expanded:
https://codesandbox.io/s/upbeat-tesla-uchsb?file=/src/MyAccordion.js
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Accordion from "#material-ui/core/Accordion";
import AccordionDetails from "#material-ui/core/AccordionDetails";
import AccordionSummary from "#material-ui/core/AccordionSummary";
import Typography from "#material-ui/core/Typography";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
background: "red",
color: "blue"
},
rootExpanded: {
background: "blue",
flexGrow: 1
}
}));
export default function MyAccordion(props) {
const classes = useStyles();
const { name } = props;
const [expanded, setExpanded] = React.useState(false);
const rootClass = expanded ? classes.rootExpanded : classes.root;
const handleChange = (panel) => (event, isExpanded) => {
setExpanded(isExpanded ? panel : false);
};
return (
<Accordion
className={rootClass}
expanded={expanded === name}
onChange={handleChange(name)}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1bh-content"
id={`${name}-header`}
>
<Typography className={classes.heading}>General settings</Typography>
<Typography className={classes.secondaryHeading}>
I am an accordion
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Nulla facilisi. Phasellus sollicitudin nulla et quam mattis feugiat.
Aliquam eget maximus est, id dignissim quam.
</Typography>
</AccordionDetails>
</Accordion>
);
}
I am using materialUI to display an Expansion Panel as shown in following code:
import React from 'react'
import ExpansionPanel from '#material-ui/core/ExpansionPanel';
import ExpansionPanelSummary from '#material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '#material-ui/core/ExpansionPanelDetails';
import Typography from '#material-ui/core/Typography';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
function ExpansionPanelDemo(props) {
const {curr} = props
return (
<div>
<ExpansionPanel id={curr.id}>
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{curr.name}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography> {curr.content} </Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
</div>
)
}
export default ExpansionPanelDemo
It works fine but by default, it displays the expand icon to the right of the screen , I want to modify this and display the icon to the left of panel.
I tried doing this using "IconButtonProps" prop given in the documentation https://material-ui.com/api/expansion-panel-summary/ and modified code as follows:
import React from 'react'
import ExpansionPanel from '#material-ui/core/ExpansionPanel';
import ExpansionPanelSummary from '#material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '#material-ui/core/ExpansionPanelDetails';
import Typography from '#material-ui/core/Typography';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
function ExpansionPanelDemo(props) {
const {curr} = props
const icon = {
float: "left"
}
return (
<div>
<ExpansionPanel id={curr.id}>
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />} IconButtonProps={icon}>
<Typography>{curr.name}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography> {curr.content} </Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
</div>
)
}
export default ExpansionPanelDemo
But something is off, the code isn't working. Can someone suggest what's wrong or any other methods to display the icon to left
ExpansionPanelSummary uses a flex display around the content and expandIcon. To re-order these, you can use either the flex-direction CSS property or the order CSS property. One other change to make this look decent is to flip the edge property on the IconButton from end to start.
Here is a working example:
import React from "react";
import { withStyles, makeStyles } from "#material-ui/core/styles";
import ExpansionPanel from "#material-ui/core/ExpansionPanel";
import ExpansionPanelSummary from "#material-ui/core/ExpansionPanelSummary";
import ExpansionPanelDetails from "#material-ui/core/ExpansionPanelDetails";
import Typography from "#material-ui/core/Typography";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
const useStyles = makeStyles(theme => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
}));
const FlippedOrderExpansionPanelSummary = withStyles({
root: {
flexDirection: "row-reverse"
}
})(ExpansionPanelSummary);
FlippedOrderExpansionPanelSummary.defaultProps = {
IconButtonProps: { edge: "start" }
};
export default function SimpleExpansionPanel() {
const classes = useStyles();
return (
<div className={classes.root}>
<ExpansionPanel>
<FlippedOrderExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography className={classes.heading}>Expansion Panel 1</Typography>
</FlippedOrderExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
malesuada lacus ex, sit amet blandit leo lobortis eget.
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel>
<FlippedOrderExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel2a-content"
id="panel2a-header"
>
<Typography className={classes.heading}>Expansion Panel 2</Typography>
</FlippedOrderExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
malesuada lacus ex, sit amet blandit leo lobortis eget.
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
</div>
);
}
Related documentation:
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Ordering_Flex_Items
I'm trying to use a Carousel slider on my website but all of the images are not showing. I'm using an example provided by a library but it's not working.
I've linked the Bootstrap CDN in my HTML file but it does not help.
import React, { Component } from 'react';
import {Carousel,} from 'react-bootstrap';
import Carousel from 'nuka-carousel';
class Slider extends Component {
render() {
return (
<Carousel autoplay={true}>
<img src="../asset/img1.jpg" />
<img src="http://placehold.it/1000x400/ffffff/c0392b/&text=slide2" />
<img src="http://placehold.it/1000x400/ffffff/c0392b/&text=slide3" />
<img src="http://placehold.it/1000x400/ffffff/c0392b/&text=slide4" />
<img src="http://placehold.it/1000x400/ffffff/c0392b/&text=slide5" />
<img src="http://placehold.it/1000x400/ffffff/c0392b/&text=slide6" />
</Carousel>
);
}
}
export default Slider;
You are using 2 different Carousels from 2 different library's I'm sure this will cause some issues.
Remove import Carousel from 'nuka-carousel';
I would stick to using react-bootstrap version
install package: npm install --save react-bootstrap
Add stylesheet to your HTML:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
Make sure you are importing it properly like so:
import {Carousel} from 'react-bootstrap';
The example from the actual react-bootstrap
class ControlledCarousel extends React.Component {
constructor(props, context) {
super(props, context);
this.handleSelect = this.handleSelect.bind(this);
this.state = {
index: 0,
direction: null
};
}
handleSelect(selectedIndex, e) {
alert(`selected=${selectedIndex}, direction=${e.direction}`);
this.setState({
index: selectedIndex,
direction: e.direction
});
}
render() {
const { index, direction } = this.state;
return (
<Carousel
activeIndex={index}
direction={direction}
onSelect={this.handleSelect}
>
<Carousel.Item>
<img width={900} height={500} alt="900x500" src="/carousel.png" />
<Carousel.Caption>
<h3>First slide label</h3>
<p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item>
<img width={900} height={500} alt="900x500" src="/carousel.png" />
<Carousel.Caption>
<h3>Second slide label</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item>
<img width={900} height={500} alt="900x500" src="/carousel.png" />
<Carousel.Caption>
<h3>Third slide label</h3>
<p>
Praesent commodo cursus magna, vel scelerisque nisl consectetur.
</p>
</Carousel.Caption>
</Carousel.Item>
</Carousel>
);
}
}
render(<ControlledCarousel />);