Text field with multiple value(image included for reference) - material-ui

I'm looking for a text field with multiple inputs as:
Here as you can see I can add new text and on press of enter it saves that keyword.
Can someone guide which package to look for.... I found something similar in material ui autocomplete's costomized hook: https://material-ui.com/components/autocomplete/,
but I don't want it to be drop down I want it to be simply be a text field with option as shown in image.
I cannot find with such functionality or I might be looking work as I don't know proper term for it.
Any guidance will be of great help.
I've included material-ui , reactstrap as this is something I have worked with so if there is any other package also let me know

I was also building something like this few days back. This is what I've built till now.
import {
Chip,
FormControl,
Input,
makeStyles,
} from "#material-ui/core";
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const classes = useStyles();
const [values, setValues] = useState(["test"]);
const [currValue, setCurrValue] = useState("");
const handleKeyUp = (e) => {
console.log(e.keyCode);
if (e.keyCode == 32) {
setValues((oldState) => [...oldState, e.target.value]);
setCurrValue("");
}
};
useEffect(() => {
console.log(values);
}, [values]);
const handleChange = (e) => {
setCurrValue(e.target.value);
};
const handleDelete = ( item, index) =>{
let arr = [...values]
arr.splice(index,1)
console.log(item)
setValues(arr)
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<FormControl classes={{ root: classes.formControlRoot }}>
<div className={"container"}>
{values.map((item,index) => (
<Chip size="small" onDelete={()=>handleDelete(item,index)} label={item}/>
))}
</div>
<Input
value={currValue}
onChange={handleChange}
onKeyDown={handleKeyUp}
/>
</FormControl>
</div>
);
}
const useStyles = makeStyles((theme) => ({
formControlRoot: {
display: "flex",
alignItems: "center",
gap: "8px",
width: "300px",
flexWrap: "wrap",
flexDirection: "row",
border:'2px solid lightgray',
padding:4,
borderRadius:'4px',
"&> div.container": {
gap: "6px",
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
},
"& > div.container > span": {
backgroundColor: "gray",
padding: "1px 3px",
borderRadius: "4px"
}
}
}));
Here is the working demo:

One possible way to do this using react-hook-form and Autocomplete using the Chip with renderTags function here is an example:
import {
Box,
TextField,
Autocomplete,
Chip,
} from '#mui/material'
...
const {
handleSubmit,
control,
formState: { errors },
} = useForm()
...
<Box mt={2}>
<Controller
control={control}
name="tags"
rules={{
required: "Veuillez choisir une réponse",
}}
render={({ field: { onChange } }) => (
<Autocomplete
defaultValue={
useCasesData?.tags ? JSON.parse(useCasesData?.tags) : []
}
multiple
id="tags-filled"
options={[]}
freeSolo
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
label={option}
{...getTagProps({ index })}
/>
))
}
onChange={(event, values) => {
onChange(values);
}}
renderInput={(params) => (
<TextField
{...params}
label="Métadonnées"
placeholder="Ecriver les métadonnées"
helperText={errors.tags?.message}
error={!!errors.tags}
/>
)}
/>
)}
/>
</Box>

Related

React Leaflet popup open after rerender

I'd like to have my markers popup open even after rerender. I have a marker showing user position so that updates all the time. After that marker rerenders that closes the popup of other marker.
My code:
const StaticReferencePoints = (props) => {
const { selectedCalibrationPoint, setSelectedCalibrationPoint, staticReferencePoints } = props;
const addToast = useToastContext();
const [snackbarOpen, setSnackbarOpen] = useState(true);
const [latitude, longitude] = useGeolocationStoreHelper(['latitude', 'longitude']);
const [map, setMap] = useState(null);
const center = [latitude, longitude];
const markerRef = useRef({});
const [selectedMarker, setSelectedMarker] = useState(null);
useEffect(() => {
// open popup if it exists
if (selectedMarker) {
markerRef.current[selectedMarker]?.openPopup();
}
}, [selectedMarker, latitude, longitude]);
useEffect(() => {
if (map) {
map.addEventListener('click', () => {
setSelectedMarker(null);
});
}
}, [map]);
const handleSnackbarClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setSnackbarOpen(false);
};
const handleDeletePoint = (point) => {
if (confirm('Are you sure you want to delete this point?')) {
Meteor.call(...) => {
if (err) {
console.log(err);
addToast('Error deleting point', 'error');
}
if (res) {
console.log(res);
addToast('Point deleted', 'success');
}
});
}
};
const SingleMarker = (point) => {
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<Marker
key={'staticPoint-' + point.point?.name}
position={point.point?.coordinates}
ref={(el) => (markerRef.current[point.point.name] = el)}
eventHandlers={{
click: () => {
setSelectedMarker(point.point.name);
},
}}
>
<Popup options={{ autoClose: false }}>
<Grid container item justifyContent={'center'} xs={12}>
<Grid item container direction={'row'} xs={12}>
<Grid item xs={1} container justifyContent={'center'} alignItems={'center'}>
{ACL.check) && (
<div>
<IconButton size="small" onClick={handleClick}>
<MoreVertIcon />
</IconButton>
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem onClick={() => handleDeletePoint(point.point)}>
<ListItemIcon>
<DeleteIcon />
</ListItemIcon>
<ListItemText primary="Delete" />
</MenuItem>
</Menu>
</div>
)}
</Grid>
<Grid item xs={10} container justifyContent={'center'} alignItems={'center'}>
<Typography>{point.point?.name}</Typography>
</Grid>
<Grid item xs={1}></Grid>
</Grid>
<Button
onClick={() => {
setSelectedCalibrationPoint(point.point);
setSelectedMarker(null);
}}
>
Select For Calibration
</Button>
</Grid>
</Popup>
</Marker>
);
};
const StaticPointMarkers = () => {
return staticReferencePoints.map((point, i) => {
return <SingleMarker key={'marker-' + i} point={point} />;
});
};
const UserPositionMarker = () => <Marker position={[latitude, longitude]} icon={userPosition}></Marker>;
return (
<Grid item xs={12}>
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
}}
open={snackbarOpen}
onClose={handleSnackbarClose}
autoHideDuration={5000}
message="Calibrate first by selecting a point from the map"
action={
<IconButton size="small" aria-label="close" color="inherit" onClick={handleSnackbarClose}>
<Close fontSize="small" />
</IconButton>
}
/>
<MapContainer
center={center}
zoom={18}
maxZoom={28}
scrollWheelZoom={false}
style={{ height: '65vh' }}
whenCreated={setMap}
>
<TileLayer
url="https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}"
maxZoom={28}
subdomains={['mt0', 'mt1', 'mt2', 'mt3']}
/>
{staticReferencePoints && <StaticPointMarkers />}
{latitude && longitude && <UserPositionMarker />}
</MapContainer>
<Grid id={'info'} item container xs={12} justifyContent={'center'} marginTop={'1rem'}>
<Typography>{`${latitude}, ${longitude}`}</Typography>
</Grid>
<Grid id={'info'} item container xs={12} justifyContent={'center'}>
<InfoIcon onClick={() => setSnackbarOpen(true)} />
</Grid>
</Grid>
);
};
export default StaticReferencePoints;
I have now set current open popup on state and on useEffect open the popup but that creates flickering.
What would be the best way to force popup stay open?
If I understand you question, the problem is that the selected marker popup flickers when the user location changes?
In general the popup should be able to stay open as long as the marker is not created again.
I can see that you have latitude and longitude as dependencies in the useEffect they are not used in the useEffect tough. This will cause the useEffect too trigger on each update on latitude and longitude.
My first suggestion is to remove them from the dependency array.
Change this:
useEffect(() => {
// open popup if it exists
if (selectedMarker) {
markerRef.current[selectedMarker]?.openPopup();
}
}, [selectedMarker, latitude, longitude]);
Secondly a good thing to do is to make sure that the popup is not already open this can be done with
To this:
useEffect(() => {
// open popup if it exists
if (selectedMarker && markerRef.current[selectedMarker]?isPopupOpen()) {
markerRef.current[selectedMarker]?.openPopup();
}
}, [selectedMarker, markerRef]);
As a side note in my experience with react-leaflet, unnecessary rerenders can cause some visual irritation such as flickering. You should strive to reduce the amount of re renders. This can be done using useMemo and useCallback hooks. Genrally passing props that are functions, arrays or object might cause rerenders even if they are the same.
From what I can see your code is deeply nested and it looks like you define component inside other components. Try to break out you code to clear and clean components instead and pass props to them instead, you can still have them all in the same file.
Using a eslint and typescript might also help you to find issues with the code.
Than you #Disco for your answer.
I managed to solve the problem with:
const StaticPointMarkers = (markers) => {
return useMemo(() => markers.map((point, i) => <SingleMarker key={'marker-' + i} point={point} />), [markers])};

React Checkboxes Filtering

I'm struggling to create Checkbox filtering in React. I want my products to be filtered by brand,I have created checkboxes dynamically from database. So each checkbox corresponds to the brand. The quantity of checkboxes equals to quantity of brands.
The problem is when I click on one of the checkboxes, the other products are disappearing from the screen, and this is what I want, but at the same time, the other checkboxes is disappearing as well, and I see only the clicked one.
Also, when I click in that checkbox again I want the products of the brand to be back on the screen.
Any ideas how to do it?
Category.js component:
/** #format */
import { useState, useEffect, createContext } from "react";
import { Link, useParams } from "react-router-dom";
import Header from "../components/Header";
import React from "react";
// Importing styles
import "./styles/Category.css";
import Footer from "../components/Footer";
import Filter from "./Filter";
export const CatgContext = createContext();
export const Category = ({ onAdd }) => {
const [products, setProducts] = useState([]);
const params = useParams();
const getProducts = async () => {
try {
const res = await fetch(`/api/products/${params.type}`);
const data = await res.json();
setProducts(data);
} catch (err) {
console.log(err);
}
};
useEffect(() => {
getProducts();
}, []);
return (
<>
<Header />
<h1>{params.type}</h1>
<aside className="catalogue-aside">
<CatgContext.Provider value={{ setProducts, products }}>
<Filter products={products} />
</CatgContext.Provider>
</aside>
<div className="category-wrapper">
{products.map((product) => {
return (
<div
className="product-card"
key={product.product_id}
id={product.product_id}
>
<Link to={`/product/${product.product_id}`}>
<img className="img-card" src={product.product_image} />
<h3 className="title-card">{product.product_type}</h3>
</Link>
<p>{product.product_brand}</p>
<p>{product.product_name}</p>
<p>{product.product_description}</p>
<p>${product.product_price}</p>
<button onClick={() => onAdd(product)}>Add to Cart</button>
</div>
);
})}
</div>
<Footer />
</>
);
};
Filter.js component:
import { useState, useContext, useEffect } from "react";
import { CatgContext } from "./Category";
const Filter = (props) => {
const { products, setProducts } = useContext(CatgContext);
const handleChange = (e, value) => {
const filteredByBrand = products.filter((product) => {
return product.product_brand === value;
});
setProducts(filteredByBrand);
};
const renderCheckboxLists = () =>
products.map((product) => (
<div className="checkbox-wrapper" key={product.product_id}>
<input
onChange={(e) => handleChange(e, product.product_brand)}
type="checkbox"
/>
<span>{product.product_brand}</span>
</div>
));
return <>{renderCheckboxLists()}</>;
};
export default Filter;
Screenshot 1
Screenshot 2

Why is timeout not working on this component?

I am using material ui on a react-typescript project and need the bio to be mostly hidden until the show more button is pressed. In the docs for Collapse from material-ui it says you can pass in a number value for timeout and it will determine the amount of time that the transition takes to expand the card. However, I have passed in a large number here and the transition still only takes less than a second. Should I be passing the timeout prop in differently?
export default function ProfileBio({ bio }: Props) {
const [expandBio, setExpandBio] = useState<boolean>(false);
const [animation, setAnimation] = useState({});
const classes = useStyles();
const [bioHeight, setBioHeight] = useState(classes.notExpanded);
// bio
const BIO = [
{
name: 'Bio',
icon: <IconStyle icon={fileTextOutline} />,
content: bio
}
];
const fields = [
{
name: 'bio',
content: bio,
displayName: 'Bio'
}
];
const handleExpandClick = () => {
setExpandBio(!expandBio);
};
const CurrentContent: Function = () => {
return BIO.map((bioField) => (
<DetailsWrapper key={bioField.name}>
<Collapse
sx={{ mt: 0 }}
collapsedSize={72}
in={expandBio}
easing="ease"
timeout={60000}
onEnter={() => console.log('entered')}
>
<Typography
variant="body2"
color="text.primary"
style={{ whiteSpace: 'pre-wrap' }}
// className={bioHeight}
>
{bioField.icon}
{bioField.content}
</Typography>
</Collapse>
</DetailsWrapper>
));
};
const ShowMoreButton: Function = () => {
if (!edit) {
return (
<ExpandMore
stretchedLink
expand={expandBio}
onClick={handleExpandClick}
aria-expanded={expandBio}
aria-label="show more"
>
<Typography variant="button">
show
{expandBio ? 'less' : 'more'}
</Typography>
<ExpandMoreIcon className="chevron" />
</ExpandMore>
);
}
return <Box />;
};
return (
<Card>
<CardHeader
title="Bio"
action={
<IconButton
onClick={() => {
setEdit(!edit);
}}
>
<CreateIcon />
</IconButton>
}
/>
<CardContent>
<CurrentContent />
</CardContent>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
backgroundColor: '#FFFFFF'
}}
>
<ShowMoreButton />
</Box>
</Card>
);
}

How to insert a subdocument into a mongo collection?

MERN stack.
There are a main storyModel and an eventModel which is an embedded subdocument. I create a story and then add events to it. I've tried several ways, searched SO for the way to insert a subdocument to an existing mongo collection, looked at the mongo and mongoose documentation(very little) and I can't seem to insert. I was able to do it before but after refactoring, the code doesn't work anymore. I tried isolating where the problem is, and it seems to be pointing to route although I'm not 100% on this.
Here is my model/schema(using mongoose):
const mongoose = require('mongoose')
const GeoSchema = mongoose.Schema({
type: {
type: String,
enum: ['Point', 'LineString', 'Polygon'],
default: "Point"
},
coordinates: {
type: [Number],
index: "2dsphere"
}
})
const EventSchema = mongoose.Schema({
eventDate: {
type: Date
},
eventTitle: {
type: String,
required: true,
minlength: 3
},
eventDescription: {
type: String,
minlength: 3
},
eventImageUrl: {
type: String
},
eventLink: {
type: String
},
eventAudio: {
type: String
},
eventLocation: GeoSchema,
})
const StorySchema = mongoose.Schema({
storyTitle: {
type: String,
minlength: 5,
required: true
},
storySummary: {
type: String
},
storyImageUrl: {
type: String,
required: true
},
storyStatus: {
type: String,
default: 'public',
enum: ['public', 'private']
},
storyCreator: {
type: mongoose.Types.ObjectId,
// required: true,
ref: 'User'
},
storyReferences: [String],
storyTags: [String],
storyMapStyle: {
type: String,
default: 'mapbox://styles/mapbox/light-v9',
},
likes: [{
type: mongoose.Types.ObjectId,
ref: 'User'
}],
event: [ EventSchema ]
}, {timestamps: true})
module.exports = mongoose.model('Story', StorySchema)
Here is my express Route:
// Create a new event for a specific story, used by AddEvent.js
router.put('/:story_id/update', (req, res) => {
const {
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio } = req.body
console.log('eventDate',eventDate)
Story.findByIdAndUpdate(
{ _id: req.params.story_id},
{
$push:
{ event:
{
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio
}}
})
})
Here is the React code just in case:
import React, {useEffect, useState} from 'react';
import { useForm } from 'react-hook-form'
import { useHistory, useParams } from 'react-router-dom'
import * as yup from 'yup'
import { yupResolver } from "#hookform/resolvers/yup"
import axios from 'axios'
import clsx from 'clsx';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import Typography from '#material-ui/core/Typography';
import { makeStyles } from '#material-ui/core/styles';
import Container from '#material-ui/core/Container';
import Collapse from '#material-ui/core/Collapse';
import InfoIcon from '#material-ui/icons/Info';
import InputAdornment from '#material-ui/core/InputAdornment';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
const schema = yup.object().shape({
eventDate: yup
.date(),
eventTitle: yup
.string()
.required('Title is a required field.')
.min(3),
eventDescription: yup
.string(),
eventImageUrl: yup
.string(),
eventLink: yup
.string(),
eventAudio: yup
.string(),
// eventType: yup
// .string(),
// eventLatitude: yup
// .number()
// .transform(cv => isNaN(cv) ? undefined : cv).positive()
// .nullable()
// .lessThan(90)
// .moreThan(-90)
// .notRequired() ,
// eventLongitude: yup
// .number()
// .transform(cv => isNaN(cv) ? undefined : cv).positive()
// .nullable()
// .lessThan(180)
// .moreThan(-180)
// .notRequired() ,
})
const useStyles = makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(12),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(2, 0, 0),
},
formControl: {
marginTop: '1rem',
},
}));
export default function AddEvent(props) {
const classes = useStyles();
const history = useHistory()
const { register, handleSubmit, errors } = useForm({
resolver: yupResolver(schema)
})
const { story_id } = useParams()
const [data, setData] = useState('')
const [expanded, setExpanded] = useState(false);
const {
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio,
eventLocation
} = data
useEffect(() => {
axios.put(`http://localhost:5000/story/${story_id}/update`, {
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio,
eventLocation
})
.then(() => history.goBack() )
.catch(err => console.log(err))
}, [data])
const handleExpandClick = () => {
setExpanded(!expanded);
};
const handleCancel = () => {
history.goBack()
};
const onSubmit = (data) => {
console.log(data);
setData(data)
}
return (
<Container component="main" maxWidth="xs">
<div className={classes.paper}>
<Typography>
Add New Event
</Typography>
<form className={classes.form} noValidate onSubmit={handleSubmit(onSubmit)}>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="eventDate"
label="eventDate"
name="eventDate"
autoComplete="eventDate"
type="text"
autoFocus
inputRef={register}
error={!!errors.eventDate}
helperText={errors?.eventDate?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
required
id="eventTitle"
label="eventTitle"
name="eventTitle"
autoComplete="eventTitle"
type="text"
inputRef={register}
error={!!errors.eventTitle}
helperText={errors?.eventTitle?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
multiline
rows={4}
name="eventDescription"
label="eventDescription"
type="text"
id="eventDescription"
inputRef={register}
error={!!errors.eventDescription}
helperText={errors?.eventDescription?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventImageUrl"
label="eventImageUrl"
type="text"
id="eventImageUrl"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<Button
className={clsx(classes.expand, {
[classes.expandOpen]: expanded,
})}
onClick={handleExpandClick}
aria-expanded={expanded}
aria-label="show more"
>
<InfoIcon size='sm' />
</Button>
</InputAdornment>
),
}}
inputRef={register}
error={!!errors.eventImageUrl}
helperText={errors?.eventImageUrl?.message}
/>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<p>Pls paste either an image or video url link here.</p>
<p>If you are using a YouTube link: Navigate to the video you wish to embed. Click the Share link below the video, then click the Embed link. The embed link will be highlighted in blue. Copy and paste this link here.
</p>
</Collapse>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventLink"
label="eventLink"
type="text"
id="eventLink"
inputRef={register}
error={!!errors.eventLink}
helperText={errors?.eventLink?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventAudio"
label="eventAudio"
type="text"
id="eventAudio"
inputRef={register}
error={!!errors.eventAudio}
helperText={errors?.eventAudio?.message}
/>
{/* <FormControl fullWidth variant="outlined" className={classes.formControl}>
<InputLabel id="demo-simple-select-outlined-label">eventType</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={eventType}
onChange={handleChange}
defaultValue={'Point'}
label="eventType"
className={classes.selectEmpty}
>
<MenuItem value={'Point'}>Point</MenuItem>
<MenuItem value={'LineString'}>LineString</MenuItem>
<MenuItem value={'Polygon'}>Polygon</MenuItem>
</Select>
</FormControl> */}
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventLatitude"
label="eventLatitude"
type="number"
id="eventLatitude"
inputRef={register}
error={!!errors.eventLatitude}
helperText={errors?.eventLatitude?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventLongitude"
label="eventLongitude"
type="number"
id="eventLongitude"
inputRef={register}
error={!!errors.eventLongitude}
helperText={errors?.eventLongitude?.message}
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Submit
</Button>
<Button
fullWidth
color="default"
onClick={handleCancel}
className={classes.submit}
>
Cancel
</Button>
</form>
</div>
</Container>
);
}
in a simple answer, you should call Story.updateOne and not Story.findByIdAndUpdate, but I would suggest a bit more (look at this as a code review thingy) ...
make use of pluralism, so the property should be called events and not event as it will have more than one
remove property prefixes, so StoryTitle would simply be title (we already know it's a Story, as it's in a Story collection), and EventTitle would be simply `event) (we already know it's an event as it's in an "events" array)
why set storyReferences as a string, should be easier manipulated if an array of string, same for storyTags
I took the liberty, just to help in your MongoDB API development, to create a very simple NodeJs project in GitHub with very simple REST calls, where you can go through and make calls to create documents and query them...
GitHub project https://github.com/balexandre/so65351733

Custom disabled style for Material UI's checkbox?

Im building a design system which uses Material UI under the hood. I need to customise the design of a disabled checkbox.
In this code why is the disabled style setting the color to gold not being applied?
import CheckboxMUI from '#material-ui/core/Checkbox';
import CheckBoxOutlineBlankIcon from '#material-ui/icons/CheckBoxOutlineBlank';
import FormControlLabel from '#material-ui/core/FormControlLabel';
const Checkbox = ({ label, onChange, checked, disabled }) => {
const theme = useTheme();
const useStyles = makeStyles({
root: {
color: theme.palette.secondary.main,
'&$disabled': {
color: 'gold',
},
},
});
const classes = useStyles();
return (
<div>
<FormControlLabel
disabled={disabled}
classes={{
root: classes.root,
}}
control={
<CheckboxMUI
disabled={disabled}
checked={checked}
onChange={onChange}
name="checkedA"
color="primary"
icon={
<CheckBoxOutlineBlankIcon
htmlColor={!disabled ? theme.palette.secondary.main : undefined}
/>
}
/>
}
label={label}
/>
</div>
);
};
Material UI's <Checkbox> allows you to provide custom elements for the icon, checkedIcon and indeterminateIcon. In your case, something along these lines would solve your problem:
import React from 'react'
import {
Checkbox as MUICheckbox,
CheckboxProps,
SvgIcon,
} from '#material-ui/core'
import { ReactComponent as CheckboxChecked } from 'assets/svg/ui/CheckboxChecked.svg'
import { ReactComponent as CheckboxUnchecked } from 'assets/svg/ui/CheckboxUnchecked.svg'
import { ReactComponent as CheckboxIndeterminate } from 'assets/svg/ui/CheckboxIndeterminate.svg'
import { ReactComponent as CheckboxDisabled } from 'assets/svg/ui/CheckboxDisabled.svg'
export default function Checkbox(props: CheckboxProps) {
return (
<MUICheckbox
checkedIcon={<SvgIcon component={CheckboxChecked} />}
indeterminateIcon={<SvgIcon component={CheckboxIndeterminate} />}
icon={
<SvgIcon
component={props.disabled ? CheckboxDisabled : CheckboxUnchecked}
/>
}
{...props}
/>
)
}
Which you would then use as follows (which is exactly how I solved this problem):
<Checkbox
disabled={someCondition}
/>
This worked for me:
const useStyles = makeStyles({
root: {
color: theme.palette.secondary.main,
},
label: {
'&.Mui-disabled': {
color: theme.colors.disabledForeground,
},
},
});
<FormControlLabel
disabled={disabled}
classes={{
root: classes.root,
label: classes.label,
}}