I'm using Tab from Semantic UI for the top of a table. It contains more tabs and I noticed in Developer tools that the selected one contains beside its normal class elements a new element active.
This is the code:
const panes = [
{
menuItem: (
<Menu.Item
className="tab-title"
key="Reference"
style={{
display: 'block',
background:'url(https://svgshare.com/i/Nnu.svg) left center no-repeat',
backgroundSize: 'cover',
minWidth: 292,
borderColor: 'transparent',
}}>
<p>Reference</p>
</Menu.Item>
),
render: () => <Tab.Pane>{referenceTab}</Tab.Pane>,
},
{
menuItem: (
<Menu.Item
className="tab-title"
key="List"
style={{
display: 'block',
background:'url(https://svgshare.com/i/Nnu.svg) left center no-repeat',
backgroundSize: 'cover',
minWidth: 300,
borderColor: 'transparent',
}}>
<p>List</p>
</Menu.Item>
),
render: () => <Tab.Pane>{listTab}</Tab.Pane>
},
];
return (
<Layout>
<TabContainer panes={panes} />
</Layout>
);
And in inspect mode, the selected tab has this class: active item tab-title while the non-selected one has item tab-title.
Is there a way to use this in code? For example I want to change the background url with another one if class contains active.
You can use Element.classList.contains("active").
MDN for classList. MDN for contains
Edit:
Alternatively, you could add css which would look like
.active.tab-title {
background-color:red;
}
Try something like this for setting a css property dynamically.
style={{
background-color: ${active ? 'red' : 'blue'},
}}>
Related
I've always been a fan of Google's search bar, with nice rounded corners and ample padding around the text.
I'm trying to replicate this style using Material UI's <Autocomplete/> component, but I can't seem to do it. I'm using Next.js. What I have so far is:
import React, { useState, useEffect } from 'react';
import TextField from '#mui/material/TextField';
import Stack from '#mui/material/Stack';
import Autocomplete from '#mui/material/Autocomplete';
import { borderRadius, Box } from '#mui/system';
import SearchIcon from '#material-ui/icons/Search';
const LiveSearch = (props) => {
const [jsonResults, setJsonResults] = useState([]);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/users`)
.then(res => res.json())
.then(json => setJsonResults(json));
}, []);
return (
<Stack sx={{ width: 400, margin: "auto"}}>
<Autocomplete
id="Hello"
getOptionLabel={(jsonResults) => jsonResults.name}
options={jsonResults}
noOptionsText="No results"
isOptionEqualToValue={(option, value) => {
option.name === value.name
}}
renderOption={(props, jsonResults) => (
<Box component="li" {...props} key={jsonResults.id}>
{jsonResults.name} - Ahhh
</Box>
)}
renderInput={(params) => <TextField {...params} label="Search users..." />}
/>
</Stack>
)
}
export default LiveSearch;
The above code should run as-is – there's an axios call in there to populate the autocomplete results too.
I've tried various way to get the <SearchIcon /> icon prefix inside the input with no success, but really I'd just be happy if I could figure out how to pad it. You can see in the google screenshot how the autocomplete lines up really well with the box, but in my version, the border-radius just rounds the element, and so it no longer lines up with the dropdown.
I'm new to Material UI, so I'm still not quite sure how to do these styles, but I think the issue is that the border is being drawn by some internal element, and although I can set the borderRadius on the component itself global CSS:
.MuiOutlinedInput-root {
border-radius: 30px;
}
I can't seem to set the padding or borders anywhere. I've also tried setting style with sx but it does nothing.
You have to look at the autocomplete css classes and override them in your component or use them in your theme, if you use one.
<Autocomplete
componentsProps={{
paper: {
sx: {
width: 350,
margin: "auto"
}
}
}}
id="Hello"
notched
getOptionLabel={(jsonResults) => jsonResults.name}
options={jsonResults}
noOptionsText="No results"
isOptionEqualToValue={(option, value) => {
option.name === value.name;
}}
renderOption={(props, jsonResults) => (
<Box component="li" {...props} key={jsonResults.id}>
{jsonResults.name} - Ahhh
</Box>
)}
renderInput={(params) => (
<TextField
{...params}
label="Search users..."
sx={{
"& .MuiOutlinedInput-root": {
borderRadius: "50px",
legend: {
marginLeft: "30px"
}
},
"& .MuiAutocomplete-inputRoot": {
paddingLeft: "20px !important",
borderRadius: "50px"
},
"& .MuiInputLabel-outlined": {
paddingLeft: "20px"
},
"& .MuiInputLabel-shrink": {
marginLeft: "20px",
paddingLeft: "10px",
paddingRight: 0,
background: "white"
}
}}
/>
)}
/>
Sandbox: https://codesandbox.io/s/infallible-field-qsstrs?file=/src/Search.js
I'm trying to figure out how to line up the edges (figured it out, see update), but this is how I was able to insert the Search icon, via renderInput and I got rid of the expand and collapse arrows at the end of the bar by setting freeSolo={true} (but this allows user input to not be bound to provided options).
import { Search } from '#mui/icons-material';
import { Autocomplete, AutocompleteRenderInputParams, InputAdornment } from '#mui/material';
...
<Autocomplete
freeSolo={true}
renderInput={(renderInputParams: AutocompleteRenderInputParams) => (
<div ref={renderInputParams.InputProps.ref}
style={{
alignItems: 'center',
width: '100%',
display: 'flex',
flexDirection: 'row'
}}>
<TextField style={{ flex: 1 }} InputProps={{
...renderInputParams.InputProps, startAdornment: (<InputAdornment position='start'> <Search /> </InputAdornment>),
}}
placeholder='Search'
inputProps={{
...renderInputParams.inputProps
}}
InputLabelProps={{ style: { display: 'none' } }}
/>
</div >
)}
...
/>
Ignore the colors and other styling, but this is what it looks like:
Update
I was able to line up the edges by controlling the border-radius via css and setting the bottom left and right to 0 and top ones to 20px.
Here's a demo:
Here are the changes I had to make in css. I also left the bottom border so there is a division between the search and the results, but you can style if however you like. (Also I'm using scss so I declared colors as variables at the top).
div.MuiAutocomplete-root div.MuiOutlinedInput-root { /* Search bar when not in focus */
border-radius: 40px;
background-color: $dark-color;
}
div.MuiAutocomplete-root div.MuiOutlinedInput-root.Mui-focused { /* Search bar when focused */
border-radius: 20px 20px 0px 0px !important;
}
div.MuiAutocomplete-root div.Mui-focused fieldset { /* fieldset element is what controls the border color. Leaving only the bottom border when dropdown is visible */
border-width: 1px !important;
border-color: transparent transparent $light-gray-color transparent !important;
}
.MuiAutocomplete-listbox { /* To control the background color of the listbox, which is the dropdown */
background-color: $dark-color;
}
div.MuiAutocomplete-popper div { /* To get rid of the rounding applied by Mui-paper on the dropdown */
border-top-right-radius: 0px;
border-top-left-radius: 0px;
}
You simply add border radius to fieldset:
<Autocomplete
sx={{ '& fieldset': { borderRadius: 33 }}}
/>
Codesandbox
I am working on a react project, where I want to conditionally render a div above an existing div that currently covers the screen with an image. I would like the existing div to reduce in height, by shrinking the size of the image, when the second div conditionally renders. The second div can have a varied height. ( e.g. list of items).
This is simplified version of what I have in my App so far:
function App() {
const [show, setShow] = useState(false);
return (
<div
style={{
border: "solid",
width: "100%",
maxHeight: "100vh"
}}
>
{show && (
<div>
<div style={{ height: "300px", backgroundColor: "red" }}></div>
<button
onClick={() => {
setShow(false);
}}
>
Close
</button>
</div>
)}
<div
style={{
display: "flex",
justifyContent: "center"
}}
>
<img
src="https://i.imgur.com/LiAUjYw.png"
alt="test"
style={{
maxWidth: "100%",
height: "auto"
}}
/>
</div>
<button onClick={() => setShow(true)}>SHow</button>
</div>
);
}
It initially looks like this: https://imgur.com/a/R0e74Xk
And on clicking the 'Show' button, it looks like this: https://imgur.com/a/Iy6osio
As you can see the bottom div gets shifted down, and goes over the container div.
I was wondering how I can make the div responsive enough to change its height when the other element is rendered.
I am fairly new to styling so any help on this would be greatly appreciated.
Thank you.
Here's one simple idea. See how the style is given to the divs like so:
<div
style={{
display: "flex",
justifyContent: "center"
}}
>
Note how the style has this weird {{ }} notation. That's because style accepts an object, and the object here is:
{
display: "flex",
justifyContent: "center"
}
You could take that object and save it as a variable somewhere, for example:
const styleWhenShow = {
display: "flex",
justifyContent: "center",
height: "100px"
}
const styleWhenNoShow = {
display: "flex",
justifyContent: "center",
height: "500px"
}
Since you already have your show state, now it's just a matter of replacing the style prop with:
style={show ? styleWhenShow : styleWhenNoShow}
In case you don't want to repeat some common styles between the show and noshow styles, you can also do something like:
const commonStyle = {
display: "flex",
justifyContent: "center"
}
const styleWhenShow = {
height: "100px"
}
const styleWhenNoShow = {
height: "500px"
}
And set the style prop like:
style={show ? { ...commonStyle, ...styleWhenShow } : { ...commonStyle, ...styleWhenNoShow }}
I am using Material UI v5 beta1 and I have been trying to customize their Autocomplete component so that the Typography color on the options changes from black to white whenever the item is selected, however, I can't figure out how to pass the style to it.
So far I have tried passing my styles on .MuiAutocomplete-option either through a styled component or a global override (see code attached) and tried every state I can think of, hover, selected, focused, even tried the Material classes for those but none of them worked. I have also tried using a custom Popper with a MenuList inside but with no luck. I have been pulling my hair with this for a couple of days now, and not being able to inspect the DOM makes it even harder, any help or tip would be highly appreciated.
Thank you in advance.
MuiAutocomplete: {
styleOverrides: {
root: {
// ...
},
option: {
padding: '5px 12px',
backgroundColor: theme.palette.background.paper,
display: 'flex',
height: 42,
borderRadius: theme.borderRadius.s,
'&:hover': {
backgroundColor: theme.palette.action.hover,
'& .Mui-Typography-root': { color: theme.palette.text.contrast },
},
'& .Mui-selected': {
backgroundColor: theme.palette.action.hover,
'& .Mui-Typography-root': { color: theme.palette.text.contrast },
},
'&.disabled': {
opacity: 0.5,
'& .Mui-Typography-root': {
color: theme.palette.text.disabled,
},
},
},
renderOption={(props, option) => {
return (
<li {...props}>
<Box display={'flex'} flexDirection={'row'}>
<Avatar size={'32'} variant={'circular'} />
<Box display={'flex'} ml={3} flexDirection={'column'}>
<Typography color={'text.primary'}>
{option.label}
</Typography>
<Typography color={'text.secondary'}>
Extra Information
</Typography>
</Box>
</Box>
</li>
);
}}
I would pass in { selected } to your renderOption, then use it to toggle your styling inline
For example:
renderOption={(props, option, { selected }) => {
return (
<li {...props}>
<Box display={'flex'} flexDirection={'row'} style={{ backgroundColor: selected ? 'red' : 'green' }}>
<Avatar size={'32'} variant={'circular'} />
<Box display={'flex'} ml={3} flexDirection={'column'}>
<Typography color={'text.primary'}>
{option.label}
</Typography>
<Typography color={'text.secondary'}>
Extra Information
</Typography>
</Box>
</Box>
</li>
);
}}
I'm wrapping all the widgets in a CardMedia component, and setting an image.
return (
<CardMedia image={bg} className={classes.bg}>
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justify="center" spacing={4}>
{products.map((product) => (
<Grid item key={product.id} xs={12} sm={12} md={12} lg={6}>
<Product product={product}></Product>
</Grid>
))}
</Grid>
</main>
</CardMedia>);
I need to make the image a little darker.
Here is the code for the styles:
export default makeStyles((theme) => ({
toolbar: theme.mixins.toolbar,
content: {
flexGrow: 1,
backgroundColor: "transparent",
/* backgroundColor: theme.palette.background.default, */
padding: theme.spacing(3),
[theme.breakpoints.down("lg")]: {
paddingRight: 200,
paddingLeft: 200,
},
[theme.breakpoints.down("xl")]: {
paddingRight: 200,
paddingLeft: 200,
},
[theme.breakpoints.down("sm")]: {
padding: theme.spacing(3),
},
},
root: {
flexGrow: 1,
},
bg: {
backgroundRepeat: "no-repeat",
backgroundAttachment: "fixed",
},
}));
Maybe there is even a simpler way to create a background image with a darker tint?
Also, I don't know if this has to do with anything, but when I set the image with CardMedia, every time I scroll to the bottom of the page there is a short annoying lag. Thanks in advance.
Hi you should try to use this for made your image darker is a simple CSS function :
.img { filter: “brightness(50%)”; }
Also change your component if you not rendering any childrens inside :
<Product><\Product>
to
<Product />
I want to use the fontAwesome + icon such that it is in the middle of a circle. I want to use it as one icon item. I read that we can use it along with the circle icon and place it inside that but I couldn't make it work.
import IconFA from 'react-native-vector-icons/FontAwesome';
<IconFA
name="plus"
size={moderateScale(30)}
color="black"
style={styles.thumbnail}
/>
{/* <IconFA
name="circle-thin"
size={moderateScale(67)}
color="white"
/> */}
thumbnail: {
height: 68,
width: 68,
position: 'relative',
},
Alternatively, I read about 'stacked' font awesome icons but couldn't understand how to use it in react native.
Reference: https://jsfiddle.net/1d7fvLy5/1/
Snack Expo:
https://snack.expo.io/9Ild0Q1zG
I want to make something like this:
I am also open to using a <Thumbnail> if I find a similar icon's link but I couldn't find any such free icon online.
The JSFiddle example that you posted creates the circle using a CSS border with border-radius to make it circular. We can do pretty much the same thing in react-native, though borderRadius in react-native can only be a fixed number and not a percent (edit: this limitation is specific to typescript since the borderRadius property has type number. Percentage strings do work at runtime).
You can tweak this code however you want, but this will get the job done. You can use IconFA and CircleBorder as two separate nested components but I also made a component IconInCircle which combines the two.
const IconInCircle = ({ circleSize, borderWidth = 2, borderColor = 'black', ...props}) => (
<CircleBorder
size={circleSize}
borderWidth={borderWidth}
borderColor={borderColor}
>
<IconFA {...props} />
</CircleBorder>
);
const CircleBorder = ({ size, borderWidth, borderColor, children }) => (
<View
style={{
width: size,
height: size,
borderRadius: 0.5 * size,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderColor,
borderWidth,
}}>
{children}
</View>
);
The IconInCircle component takes three props specific to the border: circleSize, borderWidth, and borderColor. All other props are passed through into the IconFA child component.
Basically what we are doing is placing the icon inside of a fixed-size View with a circular border and centered contents.
Now we can use it like so:
<IconInCircle
name="plus"
size={30}
color="black"
style={styles.thumbnail}
borderWidth={1}
circleSize={50}
/>
Expo Link
Try this, just adjust according to your needs, and also don't forget to support other browsers for flex.
const styles = StyleSheet.create({
thumbnail: {
height: 68,
width: 68,
position: 'relative',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
border: '1px solid #333',
borderRadius: '50%'
},
});
import IconFA from 'react-native-vector-icons/FontAwesome';
<View style={{
position:'relative',
justifyContent:'center',
alignItems:'center',
width:40,
height:40,
backgroundColor:'black'
}}>
<IconFA name='circle-thin' size={40} color='grey'/>
<IconFA name='plus' size={20} color='white' style={{position: 'absolute', zIndex: 99}} />
</View>
I am new to ReactNative, but above snippet should work in your case
Snack Expo