How to add Custom buttons group outside slider React.js - javascript

I work with react-multi-carousel, and i try to create cusstom button outside slider, but when i create custom buttons, i have some errors:
I use this code:
import React from "react";
import "./Cards.css";
import Carousel from "react-multi-carousel";
import classes from "./CarouselCards.module.css"
import ProductionCards from "./ProductionCards/ProductionCards.jsx"
const responsive = {
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 4,
paritialVisibilityGutter: 140
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 2,
paritialVisibilityGutter: 50
}
};
const ButtonGroup = ({ next, previous, goToSlide, ...rest }) => {
const { carouselState: { currentSlide } } = rest;
return (
<div className="carousel-button-group"> // remember to give it position:absolute
<ButtonOne className={currentSlide === 0 ? 'disable' : ''} onClick={() => previous()} />
<ButtonTwo onClick={() => next()} />
<ButtonThree onClick={() => goToSlide(currentSlide + 1)}> Go to any slide </ButtonThree>
</div>
);
};
const CarouselTabs = ({ data }) => {
return (
<Carousel
ssr
itemClass="image-item"
responsive={responsive}
className={classes.card}
minimumTouchDrag={80}
infinite={true}
rrows={false} renderButtonGroupOutside={true} customButtonGroup={<ButtonGroup />}
>
{data.map(item => <ProductionCards product={item} />)}
</Carousel>
);
};
export default CarouselTabs;
How i can fix this issues and have buttons outside slider?

From the error it is evident that you haven't defined ButtonOne, ButtonTwo & ButtonThree anywhere but they are being used.
From the documentation of react-multi-carousel we can customise the dots or arrows and can be customised by providing a custom component as a prop customButtonGroup.
In your implementation a custom button group is provided but the components(ButtonOne, ButtonTwo & ButtonThree) used inside are never declared.
So, you can define a custom button group something like below
const CustomButtonGroup = ({ next, previous, goToSlide, carouselState }) => {
const { totalItems, currentSlide } = carouselState;
return (
<div className="custom-button-group">
<div>Current slide is {currentSlide}</div>
<button onClick={() => previous()}>Previous slide</button>
<button onClick={() => next()}>Next slide</button>
<button
onClick={() => goToSlide(Math.floor(Math.random() * totalItems + 1))}
>
Go to a random slide
</button>
</div>
);
};
The above example is directly taken from react-multi-carousel stories, and can be modified as per the need.
I have created a sample sandbox below is the link

Related

How to recreate draggable/scrollable mui tabs in typescript

I have this javascript draggable tabs component, it is a copy from another question here: mui-tablist with react beautiful dnd not auto scrolling on drag and it has a codesandbox
My aim is to migrate to typescript and eventually create something similar to the following mockup.
Migrating to typescript: I have taken the above question/answer and merged them together as best I can
import React from 'react';
import TabContext from "#mui/lab/TabContext";
import TabPanel from "#mui/lab/TabPanel";
import { DragDropContext, Droppable, DropResult } from "react-beautiful-dnd";
import { Draggable } from "react-beautiful-dnd";
import { Stack, styled, Tab, Tabs } from "#mui/material";
interface DraggableTabProps {
label: string;
value: string;
index: number;
key: number;
child: JSX.Element;
};
const DraggableTab = ( props: DraggableTabProps ) => {
return (
<Draggable
draggableId={`${props.index}`}
index={props.index}
disableInteractiveElementBlocking
>
{(draggableProvided) => (
<div
ref={draggableProvided.innerRef}
{...draggableProvided.draggableProps}
>
{React.cloneElement(props.child, {
...props,
...draggableProvided.dragHandleProps
})}
</div>
)}
</Draggable>
);
}
const StyledTabs = styled(Tabs)();
const StyledTab = styled(Tab)();
export default function DraggableTabComponent() {
const [value, setValue] = React.useState('1');
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
setValue(newValue);
};
const [tabs, setTabs] = React.useState(
[...Array(25)].map((_, index) => ({
id: `tab${index + 1}`,
label: `Tab ${index + 1}`,
value: `${index + 1}`,
content: `Content ${index + 1}`
}))
);
const onDragEnd = (result: DropResult) => {
const newTabs = Array.from(tabs);
const draggedTab = newTabs.splice(result.source.index, 1)[0];
newTabs.splice(result.destination!.index, 0, draggedTab);
setTabs(newTabs);
};
return (
<TabContext value={value}>
<Stack>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="1" direction="horizontal">
{(droppableProvided) => (
<StyledTabs
ref={droppableProvided.innerRef}
{...droppableProvided.droppableProps}
value={value}
onChange={handleChange}
variant="scrollable"
scrollButtons
allowScrollButtonsMobile
>
{tabs.map((tab, index) => {
const child = (
<StyledTab label={tab.label} value={tab.value} key={index} />
);
return (
<DraggableTab
label={tab.label}
value={tab.value}
index={index}
key={index}
child={child}
/>
);
})}
{droppableProvided ? droppableProvided.placeholder : null}
</StyledTabs>
)}
</Droppable>
</DragDropContext>
</Stack>
{tabs.map((tab, index) => (
<TabPanel value={tab.value} key={index}>
{tab.content}
</TabPanel>
))}
</TabContext>
);
}
This displays the tabs and I am able to click each tab and view its content, but I get an error along with each click for the draggable, e.g. on the first tab:
react_devtools_backend.js:4026 react-beautiful-dnd Unable to find draggable with id: 0
Could use some help to fix this up, thanks
It's a known issue with react-beautiful-dnd package, and it occurs only in dev mode and React v18.
Strict mode checks are run in development mode only; they do not
impact the production build.
Apparently, to solve it you have to disable strict mode for the part of your application that uses your Tab component which is not the best solution, or just ignore the issue because it is not present in the production mode.

Start animation in react-spring when component is in view

I am using react-spring for animation and all the animations start once the page is loaded. I want to control the start of the animation. The desired outcome is to let the components down in the screen start the animation once they are in view (i.e the user scrolled down). The code follows something like this :
const cols = [
/*Components here that will be animated ..*/
{component: <div><p>A<p></div> , key:1},
{component: <div><p>B<p></div> , key:2},
{component: <div><p>C<p></div> , key:3},
]
export default function foocomponent(){
const [items, setItems] = React.useState(cols);
const [appear, setAppear] = React.useState(false); // Should trigger when the component is in view
const transitions = useTransition(items, (item) => item.key, {
from: { opacity: 0, transform: 'translateY(70px) scale(0.5)', borderRadius: '0px' },
enter: { opacity: 1, transform: 'translateY(0px) scale(1)', borderRadius: '20px', border: '1px solid #00b8d8' },
// leave: { opacity: 1, },
delay: 200,
config: config.molasses,
})
React.useEffect(() => {
if (items.length === 0) {
setTimeout(() => {
setItems(cols)
}, 2000)
}
}, [cols]);
return (
<Container>
<Row>
{appear && transitions.map(({ item, props, key }) => (
<Col className="feature-item">
<animated.div key={key} style={props} >
{item.component}
</animated.div>
</Col>
))}
</Row>
</Container>
);
}
I tried using appear && transitions.map(...) but unfortunately that doesn't work. Any idea how should I control the start of the animation based on a condition?
I use https://github.com/civiccc/react-waypoint for this type of problems.
If you place this hidden component just before your animation. You can switch the appear state with it. Something like this:
<Waypoint
onEnter={() => setAppear(true) }
/>
You can even specify an offset with it. To finetune the experience.
If you wish to have various sections fade in, scroll in, whatever on enter, it's actually very simple to create a custom wrapper. Since this question is regarding React Spring, here's an example but you could also refactor this a little to use pure CSS.
// React
import { useState } from "react";
// Libs
import { Waypoint } from "react-waypoint";
import { useSpring, animated } from "react-spring";
const FadeIn = ({ children }) => {
const [inView, setInview] = useState(false);
const transition = useSpring({
delay: 500,
to: {
y: !inView ? 24 : 0,
opacity: !inView ? 0 : 1,
},
});
return (
<Waypoint onEnter={() => setInview(true)}>
<animated.div style={transition}>
{children}
</animated.div>
</Waypoint>
);
};
export default FadeIn;
You can then wrap any component you want to fade in on view in this FadeIn component as such:
<FadeIn>
<Clients />
</FadeIn>
Or write your own html:
<FadeIn>
<div>
<h1>I will fade in on enter</h1>
</div>
</FadeIn>

Share State between two specific instances of the same react component React

Before y'all say global state(redux), I'd like to say one thing. I'm mapping through an array I fetched from my API. I receive images and map over them and render my Slider component. Every 2 sliders must share the same state. So, then if i move to the next slide in the first slider, then the second slider must also go to the next slide(but not any other slides). If I move to the next slide in the 5th slider, the 6th must also move to the next slide... so on.
Component where I map over slides:
<div className='image-grid'>
{screenshots.map((imagesByResolution, resIdx, screenshotResArr) => {
return imagesByResolution.map((img, scriptIdx, screenshotScriptsArr) => {
return <Slider slides={formattedSlides} />;
});
})}
</div>
Slider:
import Button from '#material-ui/core/Button';
import MobileStepper from '#material-ui/core/MobileStepper';
import { useTheme } from '#material-ui/core/styles';
import KeyboardArrowLeft from '#material-ui/icons/KeyboardArrowLeft';
import KeyboardArrowRight from '#material-ui/icons/KeyboardArrowRight';
import React from 'react';
import SwipeableViews from 'react-swipeable-views';
import { autoPlay } from 'react-swipeable-views-utils';
import { encodeImage } from '../services/images';
import useStyles from '../styles/slider';
const AutoPlaySwipeableViews = autoPlay(SwipeableViews);
export interface ISlide {
title: string;
img: ArrayBuffer;
}
interface Props {
slides: ISlide[];
}
export default function Slider(props: Props) {
console.log(props);
const { slides } = props;
const classes = useStyles();
const theme = useTheme();
const [activeSlide, setActiveSlide] = React.useState(0);
const maxSlides = slides.length;
const handleNext = () => {
setActiveSlide((prevActiveStep) => prevActiveStep + 1);
};
const handleBack = () => {
setActiveSlide((prevActiveStep) => prevActiveStep - 1);
};
const handleSlideChange = (step: number) => {
setActiveSlide(step);
};
return (
<div className={classes.root}>
<div className={classes.header}>
<h4 className={classes.title}>{slides[activeSlide].title}</h4>
</div>
<AutoPlaySwipeableViews
axis={theme.direction === 'rtl' ? 'x-reverse' : 'x'}
index={activeSlide}
onChangeIndex={handleSlideChange}
enableMouseEvents
>
{slides.map((slide, index) => (
<div key={index}>
{Math.abs(activeSlide - index) <= 2 ? (
<img className={classes.img} src={encodeImage(slide.img, 'image/png')} alt={slide.title} />
) : null}
</div>
))}
</AutoPlaySwipeableViews>
<MobileStepper
steps={maxSlides}
position='static'
variant='text'
activeStep={activeSlide}
nextButton={
<Button size='small' onClick={handleNext} disabled={activeSlide === maxSlides - 1}>
Next
{theme.direction === 'rtl' ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
</Button>
}
backButton={
<Button size='small' onClick={handleBack} disabled={activeSlide === 0}>
{theme.direction === 'rtl' ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
Back
</Button>
}
/>
</div>
);
}
If this is not possible using either some global state management library or plain ol' react state, what is the other alternative? Thanks in advance!
Pass a unique key prop to each instance of your component.
Credits: https://stackoverflow.com/a/65654818/9990676

How do i get a button to display one image instead of all the images in an array

I'm using the react-image-lighbox npm package https://www.npmjs.com/package/react-image-lightbox and i am trying to get it to work with my custom code. I have a gallery and i am trying to get it to display an image in a lightbox when you click on a button in the array. At the moment when you click on any of the buttons it opens up all of them at the same time in an array. I need to have it filtered so that it only opens the image that you clicked on not all of them.
At the moment i am playing around with filter and if statements to try and find a solution.
Example of some of the state passed down through props.
portfolioContent: [
{
id: uuidv1(),
imgURL: image01,
text: 'Copy 1',
section: 1,
work:
'https://images2.minutemediacdn.com/image/upload/c_crop,h_1193,w_2121,x_0,y_64/f_auto,q_auto,w_1100/v1565279671/shape/mentalfloss/578211-gettyimages-542930526.jpg'
},
{
id: uuidv1(),
imgURL: image02,
text: 'Copy 2',
section: 1,
work:
'https://images2.minutemediacdn.com/image/upload/c_crop,h_1193,w_2121,x_0,y_175/f_auto,q_auto,w_1100/v1554921998/shape/mentalfloss/549585-istock-909106260.jpg'
}
]
<Main portfolio={this.state.portfolioContent} />
<PortfolioWork work={this.props.portfolio} section="1" />
import React, { Component, Fragment } from 'react';
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
const images = [
'//placekitten.com/1500/500',
'//placekitten.com/4000/3000',
'//placekitten.com/800/1200',
'//placekitten.com/1500/1500',
];
export default class LightboxExample extends Component {
constructor(props) {
super(props);
this.state = {
photoIndex: 0,
isOpen: false,
};
}
render() {
const { photoIndex, isOpen } = this.state;
return (
<Fragment>
{this.props.work.map(portfolio => {
if (portfolio.section === Number(this.props.section)) {
return (
<div className="portfolio-content" key={portfolio.id}>
<img src={portfolio.imgURL} alt={portfolio.text} />
<p>{portfolio.text}</p>
<a href={portfolio.work}>See work</a>
<button type="button" onClick={() => this.setState({ isOpen: true })}>
Open Lightbox
</button>
{isOpen && (
<Lightbox
mainSrc={portfolio.imgURL}
nextSrc={images[(photoIndex + 1) % images.length]}
prevSrc={images[(photoIndex + images.length - 1) % images.length]}
onCloseRequest={() => this.setState({ isOpen: false })}
onMovePrevRequest={() =>
this.setState({
photoIndex: (photoIndex + images.length - 1) % images.length,
})
}
onMoveNextRequest={() =>
this.setState({
photoIndex: (photoIndex + 1) % images.length,
})
}
/>
)}
</div>
);
}
})}
</Fragment>
);
}
}
Clicking on a button opens up that image in a lightbox. At the moment if you click on any button it opens up all of the images at the same time in a lighbox.
I figured it out. So i created a function in the main App.js file which took in an object. And then i passed down that function to the component through props and attached it to a click event. So that when the user clicks on the button it is assigned to its corresponding object. I then set that object to state and made the selected image object the main source for the lightbox image which gets displayed.
import React, { Component, Fragment } from 'react';
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
​
export default class LightboxExample extends Component {
constructor(props) {
super(props);
​
this.state = {
isOpen: false
};
}
​
render() {
const { isOpen } = this.state;
​
return (
<Fragment>
{this.props.work.map(portfolio => {
if (portfolio.section === Number(this.props.section)) {
return (
<div key={portfolio.id}>
<div className="portfolio-content">
<img src={portfolio.imgURL} alt={portfolio.text} />
<p>{portfolio.text}</p>
<button
type="button"
onClick={() => {
this.setState({ isOpen: true });
this.props.onImageSelect(portfolio);
}}
>
See work
</button>
</div>
<div>
{isOpen && (
<Lightbox
mainSrc={this.props.selectedImage.imgURL}
onCloseRequest={() => this.setState({ isOpen: false })}
/>
)}
</div>
</div>
);
}
})}
</Fragment>
);

Material UI Styles Not Rendering

Im attempting to build a website using Material-UI and React. When attempting to use Material-UI's styling via the Hook API, it works online in codesandbox.io but does not work locally when I run it. The border radius property does not seem to update, nor do any of the properties in the button or instruction object
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import Stepper from '#material-ui/core/Stepper';
import Step from '#material-ui/core/Step';
import StepLabel from '#material-ui/core/StepLabel';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles({
root: {
width: "100%"
},
button: {
marginRight: 10,
borderRadius: 100,
fontSize: 20,
},
instructions: {
marginTop: 2,
marginBottom: 5
}
});
function getSteps() {
return ['Select campaign settings', 'Create an ad group', 'Create an ad'];
}
function getStepContent(step) {
switch (step) {
case 0:
return 'Select campaign settings...';
case 1:
return 'What is an ad group anyways?';
case 2:
return 'This is the bit I really care about!';
default:
return 'Unknown step';
}
}
function HorizontalLinearStepper() {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const [skipped, setSkipped] = React.useState(new Set());
const steps = getSteps();
function isStepOptional(step) {
return step === 1;
}
function isStepSkipped(step) {
return skipped.has(step);
}
function handleNext() {
let newSkipped = skipped;
if (isStepSkipped(activeStep)) {
newSkipped = new Set(newSkipped.values());
newSkipped.delete(activeStep);
}
setActiveStep(prevActiveStep => prevActiveStep + 1);
setSkipped(newSkipped);
}
function handleBack() {
setActiveStep(prevActiveStep => prevActiveStep - 1);
}
function handleSkip() {
if (!isStepOptional(activeStep)) {
// You probably want to guard against something like this,
// it should never occur unless someone's actively trying to break something.
throw new Error("You can't skip a step that isn't optional.");
}
setActiveStep(prevActiveStep => prevActiveStep + 1);
setSkipped(prevSkipped => {
const newSkipped = new Set(prevSkipped.values());
newSkipped.add(activeStep);
return newSkipped;
});
}
function handleReset() {
setActiveStep(0);
}
return (
<div className={classes.root}>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
const labelProps = {};
if (isStepOptional(index)) {
labelProps.optional = <Typography variant="caption">Optional</Typography>;
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} {...stepProps}>
<StepLabel {...labelProps}>{label}</StepLabel>
</Step>
);
})}
</Stepper>
<div>
{activeStep === steps.length ? (
<div>
<Typography className={classes.instructions}>
All steps completed - you&apos;re finished
</Typography>
<Button onClick={handleReset} className={classes.button}>
Reset
</Button>
</div>
) : (
<div>
<Typography className={classes.instructions}>{getStepContent(activeStep)}</Typography>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
{isStepOptional(activeStep) && (
<Button
variant="contained"
color="primary"
onClick={handleSkip}
className={classes.button}
>
Skip
</Button>
)}
<Button
variant="contained"
color="primary"
onClick={handleNext}
className={classes.button}
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
)}
</div>
</div>
);
}
export default HorizontalLinearStepper;
You can view the expected results here: https://98m6j7m314.codesandbox.io
in which the buttons border is circular after applying the borderRadius property
When using #material-ui/styles with #material-ui/core you need to follow the installation step https://v3.material-ui.com/css-in-js/basics/#migration-for-material-ui-core-users.
Here's your codesandbox link working: https://codesandbox.io/s/material-demo-rv2w1
Web browsers use cache and in some cases your changes are not reloaded. Refershing using Ctrl+f5, clearing or disabling cache in your settings may be useful.
Please attempt to see your localhost web page using another web browser & in incognito

Categories