Adding text or description to image component to pure-react-carousel - pure-react-carousel

What is the appropriate way to add a description to each ? Currently I am adding a div under and I am unable to see the text.

Use Image component with tag prop and isBgImage set tot true.
<Slide index={i}>
<Image
tag='div'
isBgImage
src={someSrc}
>
<div className={descriptionClass}>Your description here</div>
</Image>
</Slide>

You can bypass the issue with a wrapper, without using isBgImage or children native props.
Create a wrapper component and use the prop children containing the text
Use the wrapper in the Slide component, pass the image's source as a prop
Add the children content
Use the position attritbute to style your children
It would look like this:
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext, Image } from 'pure-react-carousel';
const BackgroundImage = (props) => {
return (
<>
{props.children}
<Image src={props.img} />
</>
)
};
const childrenStyle = {
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)"
}; // to center the text for example
const Carousel = () => {
return(
<div className='home-banner'>
<CarouselProvider
naturalSlideWidth={100}
naturalSlideHeight={30}
totalSlides={4}
>
<Slider>
<Slide index={0}>
<BackgroundImage img={"your_image"} >
<div>Your children text</div>
</BackgroundImage>
</Slide>
// other slides here
</Slider>
<ButtonBack>Back</ButtonBack>
<ButtonNext>Next</ButtonNext>
</CarouselProvider>
</div>
)
};

Related

Material UI select dropdown arrow keys navigation not working

I added a textfield inside mui multiple select component , now keyboard arrow up and down list navigation is not working instead,on arrow onkeydown it focuses on the div itself (scrolling on arrow keydown).
Any help is appreciated (AutoComplete is not an option)
Have tried adding autoFocus on MenuItem itself , but that starts from the last list
import * as React from "react";
import { Theme, useTheme } from "#mui/material/styles";
import MenuItem from "#mui/material/MenuItem";
import TextField from "#mui/material/TextField";
import FormControl from "#mui/material/FormControl";
import Select, { SelectChangeEvent } from "#mui/material/Select";
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
}
};
const names = [
"Oliver Hansen",
"Van Henry",
"April Tucker",
"Ralph Hubbard",
"Omar Alexander",
"Carlos Abbott",
"Miriam Wagner",
"Bradley Wilkerson",
"Virginia Andrews",
"Kelly Snyder"
];
function getStyles(name: string, personName: string[], theme: Theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium
};
}
export default function MultipleSelect() {
const theme = useTheme();
const [personName, setPersonName] = React.useState<string[]>([]);
const handleChange = (event: SelectChangeEvent<typeof personName>) => {
const {
target: { value }
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === "string" ? value.split(",") : value
);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<Select
multiple
value={personName}
onChange={handleChange}
MenuProps={MenuProps}
onKeyDown={(e) => {
if (e.key !== "Escape") {
e.stopPropagation();
}
}}
>
<div style={{ textAlign: "center", margin: "20px 0px" }}>
<TextField placeholder="type..." />
</div>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, personName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
It seems the issue is that the TextField element is a child of the Select element and is intercepting the keydown events that would normally be used to navigate the list of options. One way to fix this would be to use the onKeyDown prop on the TextField element to prevent it from intercepting the keydown events. Instead of calling e.stopPropagation(), you can call e.preventDefault() which will prevent the event from being handled by the TextField element and allow it to be handled by the Select element.
<TextField placeholder="type..." onKeyDown={e => e.preventDefault()}/>
Also to make sure that focus is on the first element in the list after the user opens the list you can use autoFocus prop on the first element of the list.
<MenuItem
key={names[0]}
value={names[0]}
style={getStyles(names[0], personName, theme)}
autoFocus
>
{names[0]}
</MenuItem>
This should allow the arrow keys to navigate the list as expected, while still allowing the user to type into the TextField.

Material UI > Backdrop > only for some subcomponent of the page

Is there any way how to enhance a backdrop from example in https://material-ui.com/components/backdrop/ to show loading circle only above the single component (in case some page has more component), not above the whole page?
Thanks for reply.
Backdrop are fixed positioned by default, that's why it covers the whole page.
To achieve the result you want, we have to change its position to absolute and contain it inside an element with relative position — this element can be your component. If you're new in CSS positions check this docs from developer.mozilla.org.
Knowing all that, we can come up with the following codes
const useStyles = makeStyles({
parent: {
position: "relative",
width: 200,
height: 200,
backgroundColor: "red",
zIndex: 0,
},
backdrop: {
position: "absolute"
}
});
export default function App() {
const classes = useStyles();
return (
<div className={classes.parent}>
<Backdrop className={classes.backdrop} open={true}>
<CircularProgress color="inherit" />
</Backdrop>
</div>
);
}
Also we have to define z-index on either parent or backdrop element to make it work. Not sure why though.
I created a codesandbox for you to play with.
The Backdrop component of Material UI is set to position: 'fixed' by default, that's why it covers the whole page.
If you want it to reside and position itself like any other component typically on the DOM, all you have to do is to reset its position back to relative, for instance:
<Backdrop open={true} sx={{ position: 'relative' }}>
<CircularProgress color="inherit" />
</Backdrop>
and you don't need to change the parent component since it should be in your case see to relative by default if you're not changing it. But if you have crazy positions going in your app here and there, then you might consider changing that as well.
i have created a custom componenet that i use if i want to block only part of the UI:
"use strict";
/** external libraries */
import React from "react";
import Backdrop from "#mui/material/Backdrop";
import CircularProgress from "#mui/material/CircularProgress";
const BlockUi = ({open, onClose, children}) => {
return (
<div style={{"position": "relative"}}>
<Backdrop
sx={{color: "#FFFFFF", zIndex: (theme) => theme.zIndex.drawer + 1, "position": "absolute"}}
open={open}
onClick={() => onClose()}
>
<CircularProgress color="inherit"/>
</Backdrop>
{children}
</div>
);
}
export default BlockUi;
and i use it like this:
"use strict";
/** external libraries */
import React from "react";
import BlockUi from "./BlockUi";
const JsonForm = ({fields, onSubmit}) => {
const [loading, setLoading] = React.useState(false)
const stopLoading = () => {
setLoading(false)
}
return (
<div>
<BlockUi open={loading} onClose={stopLoading}>
<button type="submit" onClick={() => {
console.log(loading)
setLoading(true)
}}>Submit
</button>
</BlockUi>
</div>
);
}
export default JsonForm;

How to reduce the height of Autocomplete component of Material UI?

Can anyone help me to reduce the height of Material UI Autocomplete component ? I am trying to use set the height property to 10 or 20 px though the classes property. But it does nothing. Also tried to reduce the height of the Textfield which wrapped by Autocomplete component, but when I tried to reduce the height of the Textfield component through InputProps, then the Items that were to be suggested in Autocomplete area don't display.
I am also customizing the Autocomplete component.
To reduce the height, I used the size attribute and removed the label attribute.
renderInput={(params) => <TextField {...params} variant="standard" size="small" />}
Make sure not to override the params provided by the Autocomplete component on your TextField component, which already include things like InputProps.
You'll probably want to use Autocomplete's CSS API to achieve all the customizations you're looking for. For example,
const useStyles = makeStyles(theme => ({
inputRoot: {
color: theme.palette.primary.contrastText,
},
popupIndicator: {
color: theme.palette.primary.contrastText,
},
root: {
padding: `0 ${theme.spacing(1)}px`,
},
}))
const AutocompleteWrapper = ({
value,
onChange,
options,
}) => {
const classes = useStyles()
return (
<Autocomplete
classes={classes}
options={options}
value={value}
onChange={onChange}
renderInput={(params) => <TextField {...params} variant="standard" size="small" />}
/>
)
}
Try setting the input height via the css
.MuiInputBase-input {
height: 1.5rem;
}
If you have tags inside - you will also need to set their height, otherwise they will increase their container height
I agree with #H.Hattab. For anyone using styled-components here is the example
const AutoComplete = styled(Autocomplete)`
& .MuiInputBase-input {
height: 1.5rem;
}
`;
Use the prop ListboxProps:
Like
<Autocomplete
fullWidth
disableClearable
ListboxProps={{ style: { maxHeight: 150 } }}
...
/>

MaterialUI CardMedia Image with spinner

The following component works perfectly file. I want to show a spinner until the image gets loaded. How do I do that?
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Card from '#material-ui/core/Card';
import Image from 'material-ui-image';
import CardActionArea from '#material-ui/core/CardActionArea';
import CardContent from '#material-ui/core/CardContent';
import CardMedia from '#material-ui/core/CardMedia';
import Typography from '#material-ui/core/Typography';
/**
* Media is a Card, with an image / video and a caption. url of the media is hidden from the user,
* but the user can click it to open it in a new browser
*/
const useStyles = makeStyles({
card: {
margin: '0.5rem',
maxWidth: '25%'
}
});
const Media = ({ url, caption }: any) => {
const classes = useStyles();
return (
<Card className={classes.card}>
<CardActionArea>
<CardMedia component="img" alt={caption} height="140" image={url} title={caption} />
<CardContent>
<Typography variant="body2">{caption}</Typography>
</CardContent>
</CardActionArea>
</Card>
);
};
export default Media;
The image component should be loaded regardless of image is loaded or not. We can not show the image component on the screen by setting the height of the component to 0 and and then setting the height of the image component to desired height once the image is loaded.
Then, the tracking of if the image is loaded can be done using the state.
import React, { useState } from 'react';
// Image component with loading state
export default Image = () => {
const [hasImageLoaded, setHasImageLoaded] = useState(false);
return (
<>
<img src={src} onLoad={() => setHasImageLoaded(true)} className={`${!hasImageLoaded && height-0}`} />
{ !hasImageLoaded && <div> Loading... </div> }
</>
);
}
.height-0 {
height: 0;
}
Ps. This code can be changed according to your use. The code is done considering the general use
The Material-UI CardMedia component displays the image as a background image, so you won't be able to access the onLoad property available on the DOM <img> tag. You could chooses to use an <img> instead, like this:
add to you imports:
import CircularProgress from '#material-ui/core/CircularProgress';
add to your styles:
media: {
width: '100%',
height: 140,
},
progress: {
# center spinner
}
add state and onLoad handler to your component:
const [loaded, setLoaded] = React.useState(false);
function handleImageLoad() {
setLoaded(true);
}
replace your CardMedia with:
{loaded ? (
<img
className={classes.media}
src={url}
alt={caption}
onLoad={handleImageLoad}
/>
) : (
<div className={classes.progress}>
<CircularProgress color="secondary" />
</div>
)}

How to change expansion panel icon position to the left?

In my app, the expansion arrow has to be in the left side of the panel.
But, by default it's displaying in the right side.
This :
<ExpansionPanelSummary
className={classes.panelSummary}
expandIcon={<ExpandMoreIcon />}
IconButtonProps={{edge: 'start'}}
aria-controls='panel1a-content'
id='panel1a-header'
>
Doesn't made it.
Granted, you can't (easily) change the order in which the components appear in the HTML. However, there is a way using only CSS. ExpansionPanelSummary uses display: flex; you can therefore set the order property on the icon to make it appear to the left of the content.
This can be achieved with either useStyles or withStyles (Or possibly using plain CSS, but I haven't tried it); here's how you'd go about using the latter:
import withStyles from "#material-ui/core/styles/withStyles";
const IconLeftExpansionPanelSummary = withStyles({
expandIcon: {
order: -1
}
})(ExpansionPanelSummary);
You can then write the rest of your code using IconLeftExpansionPanelSummary instead of ExpansionPanelSummary when you want the icon to appear to the left. Don't forget to set IconButtonProps={{edge: 'start'}} on the component for proper spacing.
<AccordionSummary
className={classes.accordionSummary}
classes={{
expandIcon: classes.expandIcon,
expanded: classes.expanded
}}
IconButtonProps={{
disableRipple: true
}}
></AccordionSummary>
You can add class and use flex-direction
accordionSummary: {
flexDirection: 'row-reverse'
}
It's simple
add class on <ExpansionPanelSummary> like this
<ExpansionPanelSummary className={classes.panelSummary}>
add css against this class in jss like this
panelSummary:{flexDirection: "row-reverse"},
In case using css
add class on <ExpansionPanelSummary> like this
<ExpansionPanelSummary className="panelSummary">
add css against this class in jss like this
.panelSummary{flex-direction: row-reverse;}
you can get the expansion panel icon on left by removing it from expandIcon and add it as a children in Summary something like this
<ExpansionPanel defaultExpanded={true}>
<ExpansionPanelSummary aria-controls="panel1a-content">
{this.state.expanded ? <RemoveIcon/> : <ExpandIcon />}
<Typography component='h4' variant='h4'>My Expansion Panel</Typography>
</ExpansionPanelSummary>
<ExpansionPanelsDetails />
</ExpansionPanel>
The challenge is that the order is hardcoded into the codebase and you will not be able to use the ExpansionPanel as is.
If you look at the implementation, you will find the code as below
<div className={clsx(classes.content, { [classes.expanded]: expanded })}>{children}</div>
{expandIcon && (
<IconButton
disabled={disabled}
className={clsx(classes.expandIcon, {
[classes.expanded]: expanded,
})}
edge="end"
component="div"
tabIndex={-1}
aria-hidden
{...IconButtonProps}
>
{expandIcon}
</IconButton>
)}
As you see the <div> contains the text and then the IconButton is displayed.
So, you may have to work with what's provided out of the box or create your own Component based on what material-UI provides.
Hope that helps.
You can modify the CSS class like this:
notice the absolute position, in this way you can move the div that contains the icon whatever position you want with 'left' or 'right' properties
const useStyles = makeStyles((theme) => ({
ExpansionPanelSummaryExpandedIcon: {
'& div.MuiExpansionPanelSummary-expandIcon': {
position: 'absolute',
right: '5%',
},
}
}));
and then use in the ExpansionPanelSummary
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1-content"
id="panel1bh-header"
className={classes.ExpansionPanelSummaryExpandedIcon}
>
references:
https://cssinjs.org/?v=v10.3.0
https://v4-8-3.material-ui.com/customization/components/#overriding-styles-with-classes