I have a material-ui-time-picker and I want to control this input, it works well, but I want to edit the time input from the keyboard and not when I click the input on the clock.
My code is :
import React, { Component } from "react";
import { TimePicker } from "material-ui-time-picker";
import { Input as Time, Dialog as Clock } from "#material-ui/core";
openDialog = () => this.setState({ isOpen: true });
closeDialog = () => this.setState({ isOpen: false });
handleDialogTimeChange = newValue => {
const hours = newValue
.getHours()
.toString()
.padStart(2, "0");
const minutes = newValue
.getMinutes()
.toString()
.padStart(2, "0");
const textValue = hours + ":" + minutes;
this.setState({ time: textValue });
};
handleKeyboardTimeChange = time => this.setState({ time });
createDateFromTextValue = value => {
const splitParts = value.split(":");
return new Date(1970, 1, 1, splitParts[0], splitParts[1]);
};
render() {
return (
<div>
<Time
value={this.state.time}
onChange={this.handleKeyboardTimeChange}
endAdornment={
<InputAdornment position="end">
<IconButton onClick={this.openDialog}>
<AccessTime />
</IconButton>
</InputAdornment>
}
//}
/>
<Clock maxWidth="xs" open={this.state.isOpen}>
<TimePicker
mode="24h"
value={this.createDateFromTextValue(this.state.time)}
onChange={this.handleDialogTimeChange}
autoOk={true}
cancelLabel=""
okLabel=""
placeholder=""
disableUnderline={true}
/>
</Clock>
</div>
);
}
My sandbox: https://codesandbox.io/s/vm9wm19p27
When I run it, I get this input, but when I edit his value, the input will be disappeared.
How can I fix it ?
One solution component has been provided in their Github repository. Please check this out, it's an already know issue with material-ui and has already been accepted as a solution.This is the solution provided there incase the link becomes obsolete:
'use strict';
import React, {Component} from 'react';
import {DatePicker, IconButton, TextField} from "material-ui";
import ActionDateRange from 'material-ui/svg-icons/action/date-range';
import format from 'date-fns/format'
import parse from 'date-fns/parse'
export default class DatePickerField extends Component{
constructor(props){
super(props);
this.state = {
selectedDate: new Date(),
dateText: format(new Date(), 'MM/DD/YYYY')
};
}
handleChangeDatePicker = (event, date) => {
this.setState({selectedDate: date, dateText:format(date, 'MM/DD/YYYY')});
};
handleDateInputChange = (event, value) => {
this.setState({dateText:value});
};
handleDateInputBlur = (value) => {
let parsedDate = parse(value, 'MM/DD/YYYY');
if(this.isADate(parsedDate)){
this.setState({selectedDate:parsedDate});
}
else{
this.setState({dateText:format(this.state.selectedDate, 'MM/DD/YYYY')});
}
};
isADate = (maybeDate) => {
if ( Object.prototype.toString.call(maybeDate) === "[object Date]" ) {
if ( isNaN( maybeDate.getTime() ) ) {
return false;
}
else {
return true;
}
}
else {
return false;
}
};
render(){
let dateInputWidth = "150px";
let datePickerMargin = "-185px";
return (
<div style={{display: "flex"}}>
<TextField
style={{width:dateInputWidth}}
value={this.state.dateText}
onChange={this.handleDateInputChange}
onBlur={(event) => this.handleDateInputBlur(event.currentTarget.value)}
/>
<IconButton style={{opacity:"0.65"}}
onClick={() => this.datePicker.focus()}>
<ActionDateRange />
</IconButton>
<div style={{width:"0px", height:"0px", marginLeft:datePickerMargin}}>
<DatePicker
id="dataPicker"
floatingLabelText={''}
value={this.state.selectedDate}
errorText={''}
disabled={false}
formatDate={date => { return format(date, 'MM/DD/YYYY') } }
autoOk
container="inline"
fullWidth
onChange={this.handleChangeDatePicker}
ref={c => {
this.datePicker = c
}}
/>
</div>
</div>
)
}
}
If you face the problem Cannot find prepareStyles of undefined error, please check if you have defined the theme as provider prior to any component usage, or else it won't work. Check this note:
Beginning with v0.15.0, Material-UI components require a theme to be provided. The quickest way to get up and running is by using the MuiThemeProvider to inject the theme into your application context.
And this is the sample snippet to show how to accomplish that:
In your App.js
import React from 'react';
import ReactDOM from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import MyAwesomeReactComponent from './MyAwesomeReactComponent';
const App = () => (
<MuiThemeProvider>
<MyAwesomeReactComponent />
</MuiThemeProvider>
);
ReactDOM.render(
<App />,
document.getElementById('app')
);
And in your ./MyAwesomeReactComponent.js (that is the component you want to work witk):
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const MyAwesomeReactComponent = () => (
<RaisedButton label="Default" />
);
export default MyAwesomeReactComponent;
Please refer to their official usage guide for further details.
Look at this example:
https://mui.wertarbyte.com/#timepicker
You can replace the button in the example by a TextField with an icon and only when you click on the icon open a TimePicker instead of TimeInput or you can use the TimePicker of the material-ui-pickers package
Material-ui-pickers demo: https://material-ui-pickers.dev/api/timepicker#keyboard-input
I think the TimeInput component doesn't allow this, but you can write your own component to create the exact behavior you want. Instead of importing TimeInput import { TimePicker } from the package and create a custom component.
This is in no way fool proof but it will give you the basics to continue.
Working example: https://codesandbox.io/embed/5l167pzrx
import React, { useState } from "react";
import { Button, Input, InputAdornment, IconButton, Dialog, DialogActions } from '#material-ui/core';
import { TimePicker } from 'material-ui-time-picker';
import AccessTime from '#material-ui/icons/AccessTime';
function CustomDatePicker() {
const [isOpen, setIsOpen] = useState(false);
const [value, setValue] = useState('10:10');
const openDialog = () => setIsOpen(true);
const closeDialog = () => setIsOpen(false);
const handleDialogTimeChange = (newValue) => {
const hours = newValue.getHours().toString().padStart(2, "0");
const minutes = newValue.getMinutes().toString().padStart(2, "0")
const textValue = hours + ':' + minutes;
setValue(textValue);
}
const handleKeyboardTimeChange = (event) => setValue(event.target.value);
const createDateFromTextValue = value => {
const splitParts = value.split(':');
return new Date(1970, 1, 1, splitParts[0], splitParts[1]);
}
return (
<div>
<Input
value={value}
onChange={handleKeyboardTimeChange}
endAdornment={
<InputAdornment position="end">
<IconButton onClick={openDialog}>
<AccessTime />
</IconButton>
</InputAdornment>
}
/>
<Dialog maxWidth='xs' open={isOpen}>
<TimePicker mode='24h' value={createDateFromTextValue(value)} onChange={handleDialogTimeChange} />
<DialogActions>
<Button onClick={closeDialog} color='primary'>
Cancel
</Button>
<Button onClick={closeDialog} color='primary'>
Ok
</Button>
</DialogActions>
</Dialog>
</div>
)
}
export default CustomDatePicker
You have 2 options to do this:
use <KeyboardTimePicker />
use <KeyboardDateTimePicker />
Related
I want to implement add-to favorite list functionality. But It only works on a single item I need multiple items. I use useRef Hook and use this npm package react-use-localstorage
The problem is my local storage doesn't work properly as I expected. It always updates a single item but I need it as an Array i.e [1, 2, 4, 7, 10]
If I reload my page only 3 number id will fill the heart 🤨
import React, { useRef } from "react";
import FavoriteBorder from "#mui/icons-material/FavoriteBorder";
import Favorite from "#mui/icons-material/Favorite";
import IconButton from "#mui/material/IconButton";
import useLocalStorage from 'react-use-localstorage';
const Fv = ({ id }) => {
const [storageItem, setStorageItem] = useLocalStorage(
"favourites",
JSON.stringify([])
);
//const storagedArray = useRef(JSON.parse(storageItem));
//const isFavourited = storagedArray.current.includes(id);
const isFavourited = storageItem.includes(id)
const handleToggleFavourite = () => {
if (!isFavourited) {
setStorageItem(JSON.stringify([...JSON.parse(storageItem), id]));
} else {
setStorageItem(
JSON.stringify(
JSON.parse(storageItem).filter((savedId) => savedId !== id)
)
);
}
return (
<IconButton onClick={handleToggleFavourite}>
{isFavourited ? <Favorite color="error" /> : <FavoriteBorder color="error" />}
</IconButton>
);
};
export default Fv;
Assign Component
<Fv id={product.id} />
Okay, to be perfectly honest, I don't know why your array behaves like that. Maybe it is this external library malfunctioning. You don't need to use any external libraries to manage your favourites state. Here is a solution without react-use-localstorage:
import React, { useState } from "react";
import FavoriteBorder from "#mui/icons-material/FavoriteBorder";
import Favorite from "#mui/icons-material/Favorite";
import IconButton from "#mui/material/IconButton";
const Fv = ({ id }) => {
const [storageItem, setStorageItem] = useState(() => JSON.parse(localStorage.getItem("favourites") || "[]"))
const isFavourited = storageItem.includes(id)
const handleToggleFavourite = () => {
if (!isFavourited) {
const newStorageItem = [...storageItem, id]
setStorageItem(newStorageItem);
localStorage.setItem("favourites", JSON.stringify(newStorageItem))
} else {
const newStorageItem = storageItem.filter((savedId) => savedId !== id)
setStorageItem(newStorageItem);
localStorage.setItem("favourites", JSON.stringify(newStorageItem))
}
return (
<IconButton onClick={handleToggleFavourite}>
{isFavourited ? <Favorite color="error" /> : <FavoriteBorder color="error" />}
</IconButton>
);
};
export default Fv;
I wanted to change my calendar component , from an old one to new one , they both exist on the website , but the new one isn't working, I want to make it work , when the user choose a date, it reacts with the website
this is my old one :
This is the Code :
import 'd3-transition';
import React, { Component } from 'react';
import { connect } from "react-redux";
import { setDatePrecision, nextDate, previousDate, loadWords, loadArticles } from "../redux/actions";
class DaySelector extends Component {
state = {
datePrecision: "day",
selectedDate: new Date()
};
render() {
const rthis = this.props;
const prev = () => {
rthis.previousDate();
this.props.loadWords();
this.props.loadArticles();
};
const next = () => {
rthis.nextDate();
this.props.loadWords();
this.props.loadArticles();
}
const dayPrecision = () => {
rthis.setDatePrecision("day");
this.props.loadWords();
this.props.loadArticles();
}
const monthPrecision = () => {
rthis.setDatePrecision("month");
this.props.loadWords();
this.props.loadArticles();
}
const current_date = this.props.selectedDate;
const datePrecision = this.props.datePrecision;
const year = current_date.getFullYear();
const month = current_date.getMonth() + 1;
const day = current_date.getDate();
return (
<div>
<a href="#day" onClick={dayPrecision}>day </a>
<a href="#month" onClick={monthPrecision}>month </a>
<a href="#prev" onClick={prev}><<<</a>
{datePrecision === "day" ? String(day).padStart(2, "0") + "/" : ""}{String(month).padStart(2, "0")}/{year}
<a href="#next" onClick={next}>>>></a>
</div>
);
}
}
const mapStateToProps = state => {
return {
selectedDate: state.wordsReducer.selectedDate,
datePrecision: state.wordsReducer.datePrecision,
}
};
export default connect(mapStateToProps, { setDatePrecision, nextDate, previousDate, loadWords, loadArticles })(DaySelector);
I want to replace it with this new Calendar :
this is the code of this component :
import React, { useState } from 'react';
import Calendar from 'react-calendar';
import 'react-calendar/dist/Calendar.css';
const MyCalendar = () => {
const [date, setDate] = useState(new Date());
const onChange = (date) => setDate(date);
return (
<div>
<h5 className="card-title mb-0">Calendar</h5>
<Calendar onChange={onChange} value={date} />
</div>
);
};
export default MyCalendar;
Those components are both on the website but I could not make it dynamically work,
I've install it from Here
Thank you !
Looks like you are missing to pass the prop from onChange, try:
<Calendar onChange={(value, event) => onChange(value)} value={date} />
or try:
<Calendar onChange={(value, event) => setDate(value)} value={date} />
So I have an array state which keeps tracks of the text that the user has entered in the text field. What I want to render is the text embedded in a component so they can see what text they've previously entered. The problem I have is with my Hashtags component and when I try to render it (my attempt of it is in the ternary operator).
import React from "react";
import Avatar from "#material-ui/core/Avatar";
import Chip from "#material-ui/core/Chip";
import TextField from "#material-ui/core/TextField";
import { useState } from "react";
import Button from "#material-ui/core/Button";
export default function OutlinedChips() {
const [hashtag, setHashtag] = useState("");
const [numberOfHashtags, setNumberOfHashtags] = useState(0);
const [arrayOfHashtags, addHashtag] = useState([]);
const handleDelete = () => {
console.info("You clicked the delete icon.");
};
const handleHashtagChange = event => setHashtag(event.target.value);
const handleClick = () => {
console.info("You clicked the Chip.");
};
const newHashtag = () => {
if (numberOfHashtags < 3) {
setNumberOfHashtags(numberOfHashtags + 1);
addHashtag(arrayOfHashtags => arrayOfHashtags.concat(hashtag));
} else {
console.log("Too much hashtags");
}
};
const Hashtags = arrayOfHashtags.map(h => (
<Chip
size="small"
avatar={<Avatar>#</Avatar>}
label={h}
onDelete={handleDelete}
/>
));
console.log(arrayOfHashtags);
return (
<div>
<TextField
size="small"
inputProps={{
style: { fontSize: 15 }
}}
id="outlined-multiline-static"
multiline
rows={1}
placeholder="Description"
variant="outlined"
value={hashtag}
onChange={handleHashtagChange}
/>
<Button color="primary" onClick={newHashtag}>
Create!
</Button>
{numberOfHashtags > 0 ? <Hashtags /> : ""}
</div>
);
}
Here's the sandbox https://codesandbox.io/s/material-demo-xi9hr?file=/demo.js:0-1620
I think you should call Hashtags like this :
{numberOfHashtags > 0 ? Hashtags : ""}
your Hashtags return an array, not component, so you can't call it like a component
I get dat from backend and I should classify these data according to creation date( today, yesterday, other).
I am using typescript react with ant design.
My code is working but I believe that there is a clean and better way to do achieve this feature
Here is my code:
import React, { useEffect, useState } from 'react';
import styles from './app.module.scss';
import { List } from 'antd';
import { getDataList } from '../../services';
import { Case } from '../../interfaces/Case';
import moment from 'moment';
import 'moment/locale/ar';
const App = () => {
const [todayData, setTodayData] = useState<Case[]>([]);
const [yesterdayData, setYesterdayData] = useState<Case[]>([]);
const [data, setData] = useState<Case[]>([]);
moment.locale('ar')
let todaysDate = new Date();
let yesterdayDate = new Date(todaysDate);
yesterdayDate.setDate(todaysDate.getDate() - 1);
const today = moment(todaysDate).locale('ar').format('dddd, DD MMM');
const yesterday = moment(yesterdayDate).locale('ar').format('dddd, DD MMM');
useEffect(() => {
getDataList()
.then((res) => {
const todayList = res.data.filter(function (data: Case) {
return moment(data.createdAt).locale('ar').format('dddd, DD MMM') === today;
});
const yesterdayList = res.data.filter(function (data: Case) {
return moment(data.createdAt).locale('ar').format('dddd, DD MMM') === yesterday;
});
const list = res.data.filter(function (data: Case) {
return data.createdAt !== today && data.createdAt !== yesterday;
});
setTodayData(todayList);
setYesterdayData(yesterdayList);
setData(list);
})
.catch((error) => {
});
}, []);
return (
<>
<List
className={styles['today_style']}
dataSource={todayData}
renderItem={(item) => (
<List.Item>
<h1> Hiiii today {item.createdAt} </h1>>
</List.Item>
)}
/>
<List
className={styles['yesterday_style']}
dataSource={yesterdayData}
renderItem={(item) => (
<List.Item>
<h1> Hiiii yesterday {item.createdAt} </h1>>
</List.Item>
)}
/>
<List
className={styles['style']}
dataSource={data}
renderItem={(item) => (
<List.Item>
<h1> Hiiii {item.createdAt} </h1>>
</List.Item>
)}
/>
</>
);
};
export default App;
any suggestions would be helpful,
thank you
I think that looks ok; I would just move those variables into the useEffect since its dependent on them. Make sure you also have react dev tools installed on chrome; that will give you any warnings.
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
Edit:
You could also combine your state into a single object with 3 array properties so that you only make one update call, but again, I don't think it's necessary.
I am attempting to make a multi-page form with a switch statement and I am having trouble with updating the props.values.step, what I am using as my switch statement variable, with a function I continue to get a "xyz is not a function" error attached is the code any help would be great and thank you.
<--------------App.js------------------>
import React from "react";
import MyEnhancedForm from "./pages/FormHolder";
function App() {
return (
<div className="App">
<MyEnhancedForm />
</div>
);
}
export default App;
<--------------FormHolder.js------------------>
import Form from "./Form";
import { withFormik, Field } from "formik";
import * as Yup from "yup";
import VerifyPage from "./Verifypage";
const FormHolder = props => {
function handleIncrease() {
props.values.step += 1;
}
switch (props.values.step) {
case 1:
return <Form {...props} handleIncrease={handleIncrease} />;
case 2:
return <VerifyPage {...props} />;
default:
return null;
}
};
const MyEnhancedForm = withFormik({
mapPropsToValues: () => ({ step: 1, name: "" }),
validationSchema: Yup.object().shape({
name: Yup.string()
.max(55, "Error: Name is too long.")
.min(3, "Error: Name to short.")
}),
handleSubmit: () => {}
})(FormHolder);
export default MyEnhancedForm;
<-----------Form.js--------------->
import React from "react";
import { Field } from "formik";
import { DisplayFormikState } from "./helper";
import { Card, FormGroup, Input, Label, Button } from "reactstrap";
const Form = (props, { handleIncrease }) => {
const nextStep = e => {
props.errors.name ? console.log(props.errors) : handleIncrease();
};
return (
<Card>
<FormGroup>
<Label for="name"></Label>
<Input
tag={Field}
bsSize="lg"
type="text"
name="name"
id="name"
component="input"
/>
<Button type="submit" onClick={nextStep}>
Next
</Button>
<DisplayFormikState {...props} />
</FormGroup>
</Card>
);
};
export default Form;
<--------------VerifyPage.js------------------>
Haven't made it to the verify page yet so that's why there is very little on it.
import React from "react";
import * as Yup from "yup";
import { withFormik, Field } from "formik";
import { DisplayFormikState } from "./helper";
import { Card, FormGroup, Input, Label, Button } from "reactstrap";
const VerifyPage = props => {
const prevStep = event => {
event.preventDefault();
props.handleDecrease();
};
return (
<Card>
Verify Page
<DisplayFormikState {...props} />
</Card>
);
};
export default VerifyPage;
<--------------helper.js------------------>
import React from "react";
export const DisplayFormikState = props => (
<div style={{ margin: "1rem 0" }}>
<h3 style={{ fontFamily: "monospace" }} />
<pre
style={{
background: "#f6f8fa",
fontSize: ".65rem",
padding: ".5rem"
}}
>
<strong>props</strong> = {JSON.stringify(props, null, 2)}
</pre>
</div>
);
Your problem is in Form.js:
const Form = (props, { handleIncrease }) => {
const nextStep = e => {
props.errors.name ? console.log(props.errors) : handleIncrease();
};
handleIncrease is a prop, so you should do something like this instead:
const Form = props => {
const nextStep = e => {
props.errors.name ? console.log(props.errors) : props.handleIncrease();
};
Another problem: You're mutating values.step, which will cause further issues (e.g. updating it won't cause a re-render). There's no real reason to have Formik manage step, since it's not a form input value. Instead, you could just store it in state:
const FormHolder = props => {
const [step, setStep] = React.useState(1);
function handleIncrease() {
setStep(step => step + 1);
}
function handleDecrease() {
setStep(step => step - 1);
}