How to add Custom Buttons in React Datepicker Model - javascript

I want to create a calendar like this.
The Issue is I don't know how to add Apply and Cancel button in It. I have tried multiple solutions, But failed to get desired solution.
Through this block of code of I got this.
Kindly help me to add Button in react-datepicker.
import DatePicker from 'react-datepicker'
import 'react-datepicker/dist/react-datepicker.css'
const [startDate, setStartDate] = useState(new Date())
<DatePicker
selected={startDate}
className='w-[210px] p-3 text-sm font-[Inter-Regular] outline-none text-black-100 '
onChange={(date) => {
setStartDate(date as SetStateAction<Date>)
}}
showYearPicker
dateFormat='yyyy'
yearItemNumber={20}
/>

You can pass the buttons to the datepicker widget as children.
Both are closing the modal using the datepicker widget's api.
We have access to the api through a ref we assign to the widget.
The cancel button just set the date to the original[1] date.
const originalDate = new Date(); // or get it as prop
const [startDate, setStartDate] = React.useState(originalDate);
const calRef = React.useRef();
return (
<DatePicker
ref={calRef}
selected={startDate}
shouldCloseOnSelect={false}
onChange={(date) => setStartDate(date)}
showYearPicker
dateFormat="yyyy"
yearItemNumber={20}
>
<div>
<button
onClick={() => {
setStartDate(originalDate);
calRef.current.setOpen(false);
}}
>
Cancel
</button>
<button
onClick={() => {
calRef.current.setOpen(false);
}}
>
Apply
</button>
</div>
</DatePicker>
);
https://stackblitz.com/edit/react-datepicker-footer-buttons?file=App.tsx
[1] original - in your example it would be today but if the component receives it as prop, it can be it too

Related

React useEffect acting strangely with checkboxes and previous data

So I am trying to pass in data previously selected in a form to an update version of the form. The update version of the form needs to display all previously selected data to the user and allow them to make any necessary changes.
Here is the problem I'm having. I am using a multi-select check box component. I am passing the previously selected data in to the component. When I set the selected property for component to the previously selected data using useEffect, It will let me submit the initial data or add new selections and everything functions correctly. It will not let me uncheck/remove selections. They get submitted even though they are unchecked.
If I don't use useEffect to set selected to previousData, I can not submit with the initial data but I can add and remove selections as intended. This is not acceptable though because users will most likely not make changes every time to the check boxes.
So to clarify, I can submit without making changes and add selections the first way and I can add and remove selections the second way but not submit without making changes the second way.
import { useState, useEffect } from 'react';
export default function UpdateMultiCheckBox({
title,
hint,
data,
previousData,
setSelectedData,
dotName,
}) {
const [selected, setSelected] = useState([]);
const handleChange = (event) => {
const { checked, value } = event.currentTarget;
setSelected((prev) =>
checked ? [...prev, value] : prev.filter((val) => val !== value),
);
};
{/* This is what is causing all the problems but it will not work without this without making a change to the selections */}
useEffect(() => {
let tempData = previousData?.map((a) => a.id);
setSelected(tempData);
}, [previousData]);
useEffect(() => {
setSelectedData(selected);
}, [selected]);
const difference = data?.filter(
(item) =>
!previousData?.some((itemToBeRemoved) => itemToBeRemoved.id === item.id),
);
return (
<fieldset className='space-y-5'>
<div>
<legend className='sr-only'>{title}</legend>
<label className='text-base font-medium text-brandText'>{title}</label>
<p className='text-sm leading-5 text-brandText'>
Please select all that apply.
</p>
</div>
{previousData?.map((item) => (
<div key={item.id} className='relative flex items-start'>
<div className='flex h-5 items-center'>
<input
id={item.id}
value={item.id}
defaultChecked={true}
type='checkbox'
onChange={handleChange}
className='h-4 w-4 rounded border-gray-300 text-brandPrimary focus:ring-brandPrimary'
/>
</div>
<div className='ml-3 text-sm'>
<label htmlFor={item.id} className='font-medium text-brandText'>
{item[dotName]}
</label>
<span id='comments-description' className='text-brandText'>
{hint}
</span>
</div>
</div>
))}
{difference?.map((item) => (
<div key={item.id} className='relative flex items-start'>
<div className='flex h-5 items-center'>
<input
id={item.id}
value={item.id}
type='checkbox'
onChange={handleChange}
className='h-4 w-4 rounded border-gray-300 text-brandPrimary focus:ring-brandPrimary'
/>
</div>
<div className='ml-3 text-sm'>
<label htmlFor={item.id} className='font-medium text-brandText'>
{item[dotName]}
</label>
<span id='comments-description' className='text-brandText'>
{hint}
</span>
</div>
</div>
))}
</fieldset>
);
}
If I understood correctly, the useEffect that you say is causing the problem, is setting the state with the previousData and loosing the selected state data.
If that is the case you could try the following:
useEffect(() => {
const tempData = previousData?.map((a) => a.id);
setSelected([...selected, ...tempData]);
}, [previousData]);
So now we are adding to the selected state the previousData, another way of doing it as seen in the wild:
useEffect(() => {
const tempData = previousData?.map((a) => a.id);
setSelected(prevState => [...prevState, ...tempData]);
}, [previousData]);
Which is effectively the same but in certain scenarios could be be useful when you only pass the state setter as a prop.

show react-dates airbnb daterangepicker in mobile screen vertically fullscreen

I am using react-dates package from airbnb, and I am using DateRangePicker component of this library. I want to show datepicker verically fullscreen as shown in below image
Now, My question is how can we show this dateRangePicker component in mobile screen with vertically fullscreen.
import React, { useState } from "react";
import { DateRangePicker } from "react-dates";
export const Datepicker = () => {
const [startDate, setStartDate] = useState();
const [endDate, setEndDate] = useState();
const [focusedInput, setFocusedInput] = useState();
return (
<div>
<h1> Date </h1>{" "}
<p> selected start Date is : {JSON.stringify(startDate)} </p>
<p> selected end Date is : {JSON.stringify(endDate)} </p>
{window.matchMedia("(max-width: 700px)").matches && (
<DateRangePicker
orientation="vertical"
numberOfMonths={1}
verticalHeight={800}
startDate={startDate}
startDateId="start-date"
endDate={endDate}
endDateId="end-date"
onDatesChange={({ startDate, endDate }) => {
setStartDate(startDate);
setEndDate(endDate);
}}
focusedInput={focusedInput}
onFocusChange={(focusedInput) => setFocusedInput(focusedInput)}
/>
)}
</div>
)
Demo code sandbox is Codesandbox

Load Material UI AutoComplete suggestions after user input

I have an Autocomplete component that is required to load a massive data list (up to 6000 elements) and show suggestions accordingly to the user's input.
As the data options have so many elements, whenever the user starts typing in a slow computer, it slows down and requires some time to load everything. I have to prevent it, so I came with an idea to show the user suggestions after they typed the third character. It's even giving me this error whenever the user clicks on the input box:
Warning: React instrumentation encountered an error: RangeError: Maximum call stack size exceeded console.
I need to show the suggestions after the third character input. I have tried to use the getOptionDisabled suggestion and the limitTags, but they did not work.
Here is the code:
const NameSelect = (props) => {
return (
<Dialog>
<React.Fragment>
<DialogTitle id="search-name-dialog-title">
Search name
</DialogTitle>
<DialogContent>
<Autocomplete
id="combo-box-client-select"
options={props.NameList}
value={props.preSelectedName}
getOptionLabel={(option) =>
option.Name_name +
", " +
option.country +
", " +
option.city
}
onChange={(object, value) => {
props.preSelectedNameSet(value);
}}
renderInput={(params) => (
<TextField
{...params}
label="Search name"
variant="outlined"
fullWidth
/>
)}
/>
.
.
.
</Dialog>
);
};
Can someone please help me with that approach, or suggest a better one? Thanks!
Try something like this:
<Autocomplete
inputValue={inputValue}
onInputChange={(e) => setinputValue(event.target.value)}
id="combo-box-demo"
options={values}
getOptionLabel={(option) => option}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
open={inputValue.length > 2}
/>
Use InputValue prop to trigger the auto complete drop down.
Example : auto complete dropdown
My idea is to add a state for Autocomplete current value to watch for its autoComplete property. That state will look something like this:
const [currentValue, useCurrentValue] = useState(props.preSelectedName);
so that your component will look something like this:
<Autocomplete
autoComplete={currentValue.length >= 3 ? true : false}
onChange={useCurrentValue}
...your other properties
/>
Another idea: you might wanna use setTimeout in your onChange method to slow down the re-render. But don't forget to clearTimeout before you set them.
The feature that you require is known as "Debouncing" and it is used whenever time consuming tasks occur frequently. In your case it, everytime you type the key, the suggestions are computed and this will definetely lead to lagging.
Lodash's debounce function will implement this for you.
As far as my knowledge, I am not sure whether you can implement this with MUI Autocomplete, but a custom solution you can do something like this:-
import React, { useState, useCallback } from "react";
import { _ } from "lodash";
function AutoComplete() {
const [input, setInput] = useState("");
const [suggestions, setSuggestions] = useState([]);
const updateInput = (input) => {
setInput(input);
/*_.debounce will fire the setSuggestions
and fetchSuggestions only after a gap of 3000ms */
_.debounce((input) => setSuggestions(fetchSuggestions(input), 3000));
};
return (
<div>
<input
value={input}
class="input"
onChange={(event) => updateInput(event.target.value)}
/>
<div class="suggestions">
<ul>
{suggestions?.length > 0 &&
suggestions?.map((val, idx) => (
<li class="suggestion" key={idx}>
{val}
</li>
))}
</ul>
</div>
</div>
);
}
export default AutoComplete;
You can style the components using the appropriate styles and materialize.css so that you get a functional replica of the Autocomplete component of MUI.

Datepicker Date is not changing on event handler

I am using material-UI datepicker which i followed the procedure but on event handler the date is not changing but the initial value is working fine which i give in useState. I want my datepicker to work smooth when i want to select my desire date. The example of Datepicker is also in the link
https://material-ui.com/components/pickers/#date-time-pickers
https://codesandbox.io/s/z9k3z
import {
KeyboardDatePicker,
MuiPickersUtilsProvider,
} from "#material-ui/pickers";
import DateFnsUtils from "#date-io/date-fns";
import React, { useState } from "react";
function AddCar() {
const [date, setDate] = useState(new Date());
const addCar = (e) => {
e.preventDefault();
console.log(date.toLocaleDateString());
setDate(new Date());
};
return (
<div className="addCar">
<div>
<form className="addCar__form" onSubmit={addCar}>
{/* Date */}
<div className="addCar__mainForm row ">
<div className="col-lg-3 col-md-6 col-sm-12 my-2">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
variant="inline"
format="MM-dd-yyyy"
margin="normal"
label="Select Date"
value={date}
onChange={(e) => setDate(date)}
KeyboardButtonProps={{
"aria-label": "change date",
}}
/>
</MuiPickersUtilsProvider>
</div>
</div>
<button type="submit" className="btn btn-success">
ADD CAR
</button>
</form>
</div>
</div>
);
}
export default AddCar;
You keep setting the same date from state.
const [date, setDate] = useState(new Date());
// ...
onChange={(e) => setDate(date)}
It should be:
onChange={(e) => setDate(e)}
The first parameter in DatePicker's onChange callback is the new date you've just input.
change this line
onChange={(e) => setDate(date)}
to
onChange={setDate}
Codesandbox: https://codesandbox.io/s/material-demo-forked-siueb?file=/addCar.js:939-957
You need to set the value like
onChange={(e) => setDate(e)}
Also, You don't need to set the state like this:
const [date, setDate] = useState(new Date());

Show react calendar on click

I'm using react-calendar package on my react app. Placing <Calendar/> on the file gives the open calendar on the frontend.
But I want to display the calendar if the user clicks the input field:
<div className="form-group">
<label>Start Date</label>
<input type="text"/>
<Calendar style={{display: "none"}} />
</div>
I tried through inline style to hide the calendar component and change on click. But the calendar doesn't hide with inline css also.
Gone through documentation also but didn't find any help.
It will be best you create a wrapper component around your calendar and input. This way it manages its own showCalendar state and can be reused elsewhere.
import React, { useState } from "react";
import Calendar from "react-calendar";
const ReactCalendarDemo = ({ date, setDate }) => {
const [showCalendar, setShowCalendar] = useState(false);
const handleChange = value => {
setDate(value);
setShowCalendar(false);
};
return (
<div>
<input
value={date.toLocaleDateString()}
onFocus={() => setShowCalendar(true)}
/>
<Calendar
className={showCalendar ? "" : "hide"}
value={date}
onChange={handleChange}
/>
</div>
);
};
export default ReactCalendarDemo;
You pass in your current date value and its setter as props.
Then you can toggle display in your CSS style using the hide class
.react-calendar.hide {
display: none;
}

Categories