How to mask a Material-ui FilledInput - javascript

I'm trying to mask a FilledInput Material-ui component to display as currency using this package: https://www.npmjs.com/package/react-currency-format
I tried many different approaches, but none works right. I created a codesandbox with the problem: https://codesandbox.io/s/fancy-silence-vjkxq
Right now I'm implementing this way:
function Amount({ handleChange, values, t }) {
return (
<FormControl fullWidth variant="filled">
<InputLabel htmlFor="amount">{t("Send")}</InputLabel>
<FilledInput
name="amount"
onChange={handleChange}
value={values.amount}
startAdornment={
<InputAdornment position="start">€</InputAdornment>
}
/>
</FormControl>
);
}
export default function ExchangeRateProvider() {
return (
<CurrencyFormat
customInput={Amount}
handleChange={handleAmount}
t={t}
value={values.amount}
thousandSeparator={true}
/>
)
}
The code works fine, it updates the numbers and the material-ui component is applied, however the mask doesn't work.
Thanks in advance

Something like this?
CODESANDBOX
HERE
function App() {
const [value, setValue] = useState("0");
const handleChange = event => {
const { value } = event.target;
setValue(value);
};
return (
<div className="App">
<CurrencyFormat
value={value}
onChange={handleChange}
displayType={"input"}
customInput={FilledInput}
thousandSeparator={true}
startAdornment={<InputAdornment position="start">€</InputAdornment>}
/>
</div>
);
}

Related

React chip input field

I'm using React 17 and tried to use different chip input fields like material-ui-chip-input etc. Unfortunately, all npm packages I tried do not work with react version 17.
Do you know of any component that works with this version or how I could create one myself?
Thanks in advance
Edit:
I have managed to do what I wanted, but unfortunately, I now get the following error when pressing enter / adding a new item:
index.js:1 Warning: Cannot update a component (CreatePoll) while rendering a different component (ForwardRef(Autocomplete))
Here is the codesandbox which shows the problem:
https://codesandbox.io/s/compassionate-greider-wr9wq?file=/src/App.tsx
why don't you use this instead of a new package?
Autocomplete exactly do that.
const [receivers, setReceivers] = React.useState<string[]>([]);
import Autocomplete from '#mui/material/Autocomplete';
<Autocomplete
multiple
onChange={(e, value) => setReceivers((state) => value)}
id="tags-filled"
options={top100Films.map((option) => option.title)}
defaultValue={[top100Films[13].title]}
freeSolo
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip variant="outlined" label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
label="freeSolo"
placeholder="Favorites"
/>
)}
/>
import React from 'react'
import { MuiChipsInput } from 'mui-chips-input'
const MyComponent = () => {
const [chips, setChips] = React.useState([])
const handleChange = (newChips) => {
setChips(newChips)
}
return (
<MuiChipsInput value={chips} onChange={handleChange} />
)
}
Source:
https://github.com/viclafouch/mui-chips-input
Supports both React 17 / 18 and MUI V5
Use the freeSolo prop of Autocomplete, and just set the options prop to an empty array:
<Autocomplete
multiple
freeSolo
options={[]}
renderTags={(value, getTagProps) =>
value.map(option, index) => (
<Chip label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Add Chip"
placeholder="Type and press enter"
/>
)}
/>

How to validate click one per browser?

How can i reduce raiting to be able to rate only once per browser?
I need to make validation for raiting component in React where client can rate only once per browser.
Here is example of the rating component:
export default function App() {
const [value, setValue] = React.useState(0);
return (
<div>
<Box component="fieldset" mb={3} borderColor="transparent">
<Typography component="legend">Rate</Typography>
<Rating
name="pristine"
value={value}
onClick={(e) => setValue(e.target.value)}
/>
</Box>
</div>
);
}
Here is sandbox link of the component sandbox link
One possible solution you could think of is to use localStorage
For every user, who has already rate, you then store a boolean to the local storage.
Then next time, it will be validated first
Something like this:
export default function App() {
const [value, setValue] = React.useState(0);
const hasRated = localStorage.getItem("hasRated");
const handleRate = (e) => {
setValue(e.target.value);
localStorage.setItem("hasRated", true);
};
return (
<div>
<Box component="fieldset" mb={3} borderColor="transparent">
<Typography component="legend">Rate</Typography>
<Rating
disabled={hasRated}
name="pristine"
value={value}
onClick={handleRate}
/>
</Box>
</div>
);
}
Please note, that this is only a demonstrated example, so it supposes that you will need to have some improvement based on your needs
Sanbox Example:

React async timing issue, cause modal to pop up with prefilled form after state change?

I have a setCurrentProject call using useState, useEffect hooks api but the modal pops up before the state has been set and it causes the data to throw an error or be blank if i throw in a conditional to check if it's there.
How do I make sure the state changes before the modal opens with the correct prop data
const EditProjectModal = ({modalOpen, handleClose, addTask, currentProject}) => {
const [name, setName] = useState("");
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [projectTasks, setProjectTasks] = useState([]);
useEffect(() => {
setName(currentProject.name)
setProjectTasks(currentProject.tasks)
setStartDate(currentProject.startDate)
setEndDate(currentProject.endDate)
}, [currentProject])
return(
<Modal
centered={false}
open={modalOpen}
onClose={handleClose}
>
<Modal.Header>Edit Project</Modal.Header>
<Modal.Content>
<Form onSubmit={handleSubmit}>
<Form.Group widths="equal">
<Form.Field
control={Input}
label="Project"
placeholder="Enter the name of your project..."
required
value={name}
onChange={handleName}
/>
</Form.Group>
<Form.Group widths='equal'>
<Form.Field required>
<label>Start Date</label>
<DatePicker
selected={startDate}
onChange={date => setStartDate(date)}
selectsStart
startDate={startDate}
endDate={endDate}
/>
</Form.Field>
<Form.Field required>
<label>End Date</label>
<DatePicker
selected={endDate}
onChange={date => setEndDate(date)}
selectsEnd
startDate={startDate}
endDate={endDate}
minDate={startDate}
placeholderText="Select end date.."
/>
</Form.Field>
</Form.Group>
<ProjectTasksContainer projectTasks={projectTasks} setProjectTasks={setProjectTasks}/>
<Divider hidden/>
<Button type='submit'>Submit</Button>
</Form>
</Modal.Content>
</Modal>
I've left out some of the change functions etc because that's not important to the state change.
It's being triggered by a button from another component
const handleEdit = () => {
setCurrentProject(project)
handleEditProjClick();
}
edited to add dashboard that uses the editProjectModal
const Dashboard = ({notes, setNotes, tasks, setTasks, goals, setGoals, projects, setProjects}) => {
//============== STATE VARIABLES ===================//
const [projModalOpen, setProjModalOpen] = useState(false);
const [editProjModalOpen, setEditProjModalOpen] = useState(false);
const [currentProject, setCurrentProject] = useState({});
//============== MODALS ===================//
const handleProjModalOpen = () => {
setProjModalOpen(true);
}
const handleProjModalClose = () => {
setProjModalOpen(false);
}
const handleEditProjModalOpen = () => {
setEditProjModalOpen(true);
}
const handleEditProjModalClose = () => {
setEditProjModalOpen(false);
}
//============== RENDERING FUNCTION ===================//
return (
<Fragment>
<Grid columns={2}>
<Grid.Row stretched>
<Grid.Column width={12}>
<Container style={{width: "90%"}}>
<Segment basic>
<Segment textAlign="right">
<ProjectButton handleClick={handleProjModalOpen}/>
<ProjectModal handleClose={handleProjModalClose} modalOpen={projModalOpen} addTask={addTask} addProject={addProject} />
<EditProjectModal handleClose={handleEditProjModalClose} modalOpen={editProjModalOpen} addTask={addTask} currentProject={currentProject} />
</Segment>
</Container>
</Grid.Column>
<Grid.Column width={4}>
<Segment>
<ProgressContainer projects={projects} handleProjClick={handleProjModalOpen} handleEditProjClick={handleEditProjModalOpen} setCurrentProject={setCurrentProject}/>
</Segment>
</Grid.Column>
</Grid.Row>
</Grid>
</Fragment>
)
}
the parent holds the setCurrentProject state and passes it down to that child and also passes down the handleEditProjClick();
What's the best way to handle this issue?
Cheers!
Because editProjModalOpen and currentProject are independent you could get a situation where editProjModalOpen is true and currentProject is {} - you'd need to account for that, e.g. {!isEmpty(currentProject) && <EditProjectModal ... /.>}
This would then guarantee currentProject is set in EditProjectModal and you could just set the initial values in state off the currentProject:
const [name, setName] = useState(currentProject.name);
const [startDate, setStartDate] = useState(currentProject.startDate);
Or if you have to useEffect then just check the state before rendering the modal:
if (!name ) {
return null
}

Select all and Select None buttons in Autocomplete Material UI React

I want to implement two buttons Select All and Select None inside Autocomplete React Material UI along with checkbox for each option.When Select All button is clicked all the options must be checked and when I click Select None all the options must be unchecked.
How do I implement that ?
<Autocomplete
id={id }
size={size}
multiple={multiple}
value={value}
disabled={disabled}
options={items}
onChange={handleChange}
getOptionLabel={option => option.label}
renderOption={(option, { selected }) => (
<React.Fragment >
{isCheckBox(check, selected)}
{option.label}
</React.Fragment>
)}
renderInput={params => (
<TextField id="dropdown_input"
{...params} label="controlled" variant={variant} label={label} placeholder={placeholder} />
)}
/>
export function isCheckBox(check, selected) {
if (check) {
const CheckBox = <Checkbox
id="dropdown_check"
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
return CheckBox;
}
return null;
}
I stumbled into the same issue earlier today.
The trick is to use local state to manage what has been selected, and change the renderOption to select * checkboxes if the local state has the 'all' key in it.
NB: At the time of writing React 16 is what I'm working with
I'm on a deadline, so I'll leave a codesandbox solution for you instead of a rushed explanation. Hope it helps :
Select All AutoComplete Sandbox
Updated
for React version 16.13.1 and later. codesandbox
const [open, setOpen] = useState(false);
const timer = useRef(-1);
const setOpenByTimer = (isOpen) => {
clearTimeout(timer.current);
timer.current = window.setTimeout(() => {
setOpen(isOpen);
}, 200);
}
const MyPopper = function (props) {
const addAllClick = (e) => {
clearTimeout(timer.current);
console.log('Add All');
}
const clearClick = (e) => {
clearTimeout(timer.current);
console.log('Clear');
}
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
return (
<Autocomplete
PopperComponent={MyPopper}
onOpen={(e) => {
console.log('onOpen');
setOpenByTimer(true);
}}
onClose={(obj,reason) => {
console.log('onClose', reason);
setOpenByTimer(false);
}}
open={open}
.....
....
/>
);
Old Answer
Just customise PopperComponent and do whatever you want.
Autocomplete API
const addAllClick = (e: any) => {
setValue(items);
};
const clearClick = (e: any) => {
setValue([]);
};
const MyPopper = function (props: any) {
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
<Autocomplete
PopperComponent={MyPopper}
...
/>
If you want to make an autocomplete with select all option using react material ui and react hook form, you can implement to Autocomplete like so
multiple: To allow multiple selection
disableCloseOnSelect: To disable the close of the box after each selection
options: Array of items of selection
value: Selected options.
getOptionLabel: The string value of the option in our case is name
filterOptions: A function that determines the filtered options to be rendered on search, in our case we used it to add selectAll checkbox.
renderOption: Render the option, use getOptionLabel by default.
renderInput: To render the input,
onChange: Callback fired when the value changes
Now you can play with selected values using handleChange so once the select is fired check if the selected option is select all if yes then set the newest selectedOptions
<Autocomplete
multiple
disableCloseOnSelect
options={items}
value={selectedOptions}
getOptionLabel={(option) => option.name}
filterOptions={(options, params) => {
const filtered = filter(options, params)
return [{ id: 0, name: selectAllLabel }, ...filtered]
}}
renderOption={(props, option, { selected }) => {
// To control the state of 'select-all' checkbox
const selectAllProps =
option.name === 'Sélectionner Tous' ? { checked: allSelected } : {}
return (
<li {...props}>
<Checkbox checked={selected} {...selectAllProps} />
{option.name}
</li>
)
}}
renderInput={(params) => (
<TextField {...params} label={label} placeholder={label} />
)}
onChange={handleChange}
/>
you can refer to the Autocomplete API to get detailed definition of each item
You can refer to this codeSendBox to check a demo of react material Autocomplete with select all using react material ui version 5 and react hook form verion 7

Material ui Autocomplete press enter to create new chips

I wish I could do such a thing using Autocomplete of material ui: wertarbyte
That is, inserting text (string) without having a list of elements from which you must select.
Therefore the noOptions message should not appear, every time the enter key is pressed on the keyboard the text is inserted.
Link: codesandbox
Code:
import React from "react";
import Chip from "#material-ui/core/Chip";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
const useStyles = makeStyles(theme => ({
root: {
width: 500,
"& > * + *": {
marginTop: theme.spacing(3)
}
}
}));
export default function Tags() {
const classes = useStyles();
return (
<div className={classes.root}>
<Autocomplete
multiple
id="tags-outlined"
options={[]}
defaultValue={["foo", "bar"]}
//getOptionLabel={(option) => option}
//defaultValue={[top100Films[13]]}
//filterSelectedOptions
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
</div>
);
}
In case you have simple elements (not objects, just strings), and you don't really need to handle state (your autocomplete is not controlled) you can use the freeSolo prop of the Autocomplete.
<Autocomplete
multiple
freeSolo
id="tags-outlined"
options={["foo", "bar"]}
defaultValue={["foo", "bar"]}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
In case your elements are more complex and you do need to control the element:
Make sure the Autocomplete tag is a controlled one (you manage to value).
Listen to key down event on the TextField.
If the code is Enter (e.code === 'Enter') - take the value of the input and push it to the list of the current values that you have.
Make sure you also handle the onChange to handle the removal of elements:
Here is the code:
const [autoCompleteValue, setAutoCompleteValue] = useState(["foo", "bar"]);
return (
<Autocomplete
multiple
id="tags-outlined"
options={[]}
value={autoCompleteValue}
onChange={(e, newval, reason) => {
setAutoCompleteValue(newval);
}}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
onKeyDown={e => {
if (e.code === 'enter' && e.target.value) {
setAutoCompleteValue(autoCompleteValue.concat(e.target.value));
}
}}
/>
)}
/>
);
Check the live working example of both options: https://codesandbox.io/s/mui-autocomplete-create-options-on-enter-gw1jc
For anyone who wants to input the current best match on enter key press (as opposed to any custom text) you can use the autoHighlight prop.
<Autocomplete
multiple
autoHighlight
id="tags-outlined"
options={["foo", "bar"]}
defaultValue={["foo", "bar"]}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
To do this, don't use the Autocomplete element from MUI. Just use a a standard TextField with the use of InputProps. All you need to do is add a onKeyDown listener to the TextField that listens for 'Enter' and when the function is triggered, have it add to an array of Chips in the InputProps. It might look something like this:
const [inputValue, setInputValue] = useState('');
const [chips, setChips] = useState([])
const inputChange = ({target: {value}}) => {setInputValue(value)};
const handleKeyDown = ({key}) => {
if(key === 'Enter') {
setChips([...chips, inputValue])
}
};
<TextField
fullWidth
variant="outlined"
label="Fish and Chips"
value={inputValue}
onChange={inputChange}
multiline
InputProps={{
startAdornment: chips.map((item) => (
<Chip
key={item}
label={item}
/>
)),
}}
/>
This is untested as written here, but it should work. I've done something similar in one of my apps.

Categories