mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-20 15:42:16 +03:00
367 lines
8.6 KiB
JavaScript
367 lines
8.6 KiB
JavaScript
import * as React from "react";
|
|
import * as Constants from "~/common/constants";
|
|
import * as SVG from "~/components/system/svg";
|
|
|
|
import { css, keyframes } from "@emotion/react";
|
|
import moment from "moment";
|
|
|
|
import { Input } from "~/components/system/components/Input";
|
|
|
|
const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map(
|
|
(day, i) => <div key={i}>{day}</div>
|
|
);
|
|
|
|
const expand = keyframes`
|
|
0% {
|
|
max-height: 0px;
|
|
overflow: hidden;
|
|
}
|
|
100% {
|
|
max-height: 400px;
|
|
overflow: hidden;
|
|
}
|
|
`;
|
|
|
|
const STYLES_DATE_INPUT = css`
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
`;
|
|
|
|
const STYLES_HIDDEN_INPUT = css`
|
|
box-sizing: border-box;
|
|
opacity: 0;
|
|
position: absolute;
|
|
top: 2px;
|
|
left: 2px;
|
|
`;
|
|
|
|
const STYLES_CALENDAR = css`
|
|
box-sizing: border-box;
|
|
position: absolute;
|
|
font-family: ${Constants.font.text};
|
|
max-width: 480px;
|
|
min-width: 320px;
|
|
width: 100%;
|
|
background-color: ${Constants.system.white};
|
|
border: 1px solid ${Constants.system.darkGray};
|
|
border-radius: 4px;
|
|
padding: 5px;
|
|
animation: ${expand} 200ms ease-out 1;
|
|
z-index: ${Constants.zindex.modal};
|
|
`;
|
|
|
|
const STYLES_MONTH_CONTAINER = css`
|
|
box-sizing: border-box;
|
|
display: grid;
|
|
grid-template-columns: 30px 1fr 30px;
|
|
align-items: center;
|
|
`;
|
|
|
|
const STYLES_MONTH = css`
|
|
box-sizing: border-box;
|
|
text-align: center;
|
|
font-size: ${Constants.typescale.lvl1};
|
|
margin: 10px 0;
|
|
`;
|
|
|
|
const STYLES_WEEKDAYS = css`
|
|
box-sizing: border-box;
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
text-align: center;
|
|
width: 100%;
|
|
color: ${Constants.system.darkGray};
|
|
margin: 10px 0;
|
|
font-size: 0.9em;
|
|
border-bottom: 1px solid ${Constants.system.gray};
|
|
padding-bottom: 10px;
|
|
`;
|
|
|
|
const STYLES_DAY = css`
|
|
box-sizing: border-box;
|
|
font-size: 0.9em;
|
|
cursor: pointer;
|
|
|
|
:hover {
|
|
color: ${Constants.system.brand} !important;
|
|
}
|
|
`;
|
|
|
|
const STYLES_CHOSEN_DAY = css`
|
|
box-sizing: border-box;
|
|
font-size: 0.9em;
|
|
position: relative;
|
|
cursor: pointer;
|
|
|
|
:hover {
|
|
color: ${Constants.system.brand} !important;
|
|
}
|
|
|
|
&:after {
|
|
position: absolute;
|
|
top: calc(50% - 15px);
|
|
left: calc(50% - 15px);
|
|
content: "";
|
|
width: 30px;
|
|
height: 30px;
|
|
border-radius: 50%;
|
|
border: 1px solid ${Constants.system.brand};
|
|
}
|
|
`;
|
|
|
|
const STYLES_DISABLED_DAY = css`
|
|
box-sizing: border-box;
|
|
font-size: 0.9em;
|
|
cursor: not-allowed;
|
|
color: ${Constants.system.gray} !important;
|
|
`;
|
|
|
|
const STYLES_DATES = css`
|
|
box-sizing: border-box;
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
grid-template-rows: repeat(6, 40px);
|
|
align-items: center;
|
|
text-align: center;
|
|
width: 100%;
|
|
color: ${Constants.system.darkGray};
|
|
margin: 5px 0;
|
|
`;
|
|
|
|
const STYLES_ICON = css`
|
|
box-sizing: border-box;
|
|
cursor: pointer;
|
|
margin: 0 10px;
|
|
|
|
:hover {
|
|
color: ${Constants.system.brand};
|
|
}
|
|
`;
|
|
|
|
export class DatePicker extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.myInput = React.createRef();
|
|
this.state = {
|
|
value: "",
|
|
validation: null,
|
|
cal: null,
|
|
};
|
|
}
|
|
|
|
componentDidMount = () => {
|
|
if (moment(this.props.value).isValid()) {
|
|
this.processChange(moment(this.props.value).format("MM/DD/YYYY"));
|
|
}
|
|
};
|
|
|
|
_handleChange = (e) => {
|
|
this.processChange(e.target.value);
|
|
};
|
|
|
|
selectDay = (day) => {
|
|
if (!this.isDisabled(day)) {
|
|
this.setState({
|
|
value: day.format("MM/DD/YYYY"),
|
|
cal: null,
|
|
});
|
|
this.registerChange(day.format("YYYY-MM-DD"));
|
|
}
|
|
};
|
|
|
|
processChange = (value) => {
|
|
let validation = null;
|
|
let date = "";
|
|
let cal = this.state.cal;
|
|
if (value.length === 10) {
|
|
let result = this.checkInput(value);
|
|
if (moment.isMoment(result)) {
|
|
validation = "SUCCESS";
|
|
date =
|
|
value.substring(6, 10) +
|
|
"-" +
|
|
value.substring(0, 2) +
|
|
"-" +
|
|
value.substring(3, 5);
|
|
if (cal) {
|
|
cal = moment(date).date(1);
|
|
}
|
|
} else {
|
|
validation = result;
|
|
}
|
|
}
|
|
this.setState({ value, validation, cal }, () => {
|
|
if (cal) this.setCalendar();
|
|
});
|
|
this.registerChange(date);
|
|
};
|
|
|
|
checkInput = (value) => {
|
|
if (value.length !== 10 || !/\d{2}\/\d{2}\/\d{4}/.test(value)) return;
|
|
let date = moment(
|
|
value.substring(6, 10) +
|
|
"-" +
|
|
value.substring(0, 2) +
|
|
"-" +
|
|
value.substring(3, 5)
|
|
);
|
|
if (
|
|
!moment.isMoment(date) ||
|
|
!date.isValid() ||
|
|
date.date() !== Number(value.substring(3, 5))
|
|
)
|
|
return "ERROR";
|
|
if (this.isDisabled(date)) {
|
|
return "WARNING";
|
|
}
|
|
return date;
|
|
};
|
|
|
|
_handleBlur = (e) => {
|
|
if (!this.state.validation) {
|
|
let validation = this.checkInput(e);
|
|
this.setState({ validation });
|
|
}
|
|
};
|
|
|
|
_handleCalendar = () => {
|
|
if (this.state.cal) {
|
|
this.setState({ cal: null });
|
|
} else {
|
|
this.setState(
|
|
{
|
|
cal: this.props.value
|
|
? moment(this.props.value).date(1)
|
|
: moment().date(1),
|
|
},
|
|
this.setCalendar
|
|
);
|
|
}
|
|
};
|
|
|
|
setCalendar = () => {
|
|
let month = [];
|
|
let offset = this.state.cal.day();
|
|
for (let i = -offset; i < -offset + 6 * 7; i++) {
|
|
month.push(moment(this.state.cal).add(i, "days"));
|
|
}
|
|
this.setState({ month });
|
|
};
|
|
|
|
isDisabled = (day) => {
|
|
if (
|
|
(this.props.min && day.isBefore(moment(this.props.min))) ||
|
|
(this.props.max && day.isAfter(moment(this.props.max)))
|
|
)
|
|
return true;
|
|
if (this.props.isDisabled) {
|
|
return this.props.isDisabled(day.toDate());
|
|
}
|
|
return false;
|
|
};
|
|
|
|
registerChange = (date) => {
|
|
var myInput = this.myInput.current;
|
|
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
|
window.HTMLInputElement.prototype,
|
|
"value"
|
|
).set;
|
|
nativeInputValueSetter.call(myInput, date);
|
|
var inputEvent = new Event("input", { bubbles: true });
|
|
myInput.dispatchEvent(inputEvent);
|
|
this.props.onChange(inputEvent);
|
|
};
|
|
|
|
getStyle = (day) => {
|
|
if (this.isDisabled(day)) return STYLES_DISABLED_DAY;
|
|
if (this.props.value && day.isSame(moment(this.props.value), "day"))
|
|
return STYLES_CHOSEN_DAY;
|
|
return STYLES_DAY;
|
|
};
|
|
|
|
_incrementCal = (count) => {
|
|
let cal = this.state.cal;
|
|
cal.add(count, "months");
|
|
this.setState({ cal }, this.setCalendar);
|
|
};
|
|
|
|
render() {
|
|
let month;
|
|
if (this.state.month) {
|
|
month = this.state.month.map((day) => (
|
|
<div
|
|
key={day.toString()}
|
|
style={{
|
|
color: day.isSame(this.state.cal, "month")
|
|
? Constants.system.black
|
|
: Constants.system.darkGray,
|
|
}}
|
|
css={
|
|
this.isDisabled(day)
|
|
? STYLES_DISABLED_DAY
|
|
: this.props.value && day.isSame(moment(this.props.value), "day")
|
|
? STYLES_CHOSEN_DAY
|
|
: STYLES_DAY
|
|
}
|
|
onClick={() => this.selectDay(day)}
|
|
>
|
|
{day.date()}
|
|
</div>
|
|
));
|
|
}
|
|
return (
|
|
<div css={STYLES_DATE_INPUT}>
|
|
<input
|
|
ref={this.myInput}
|
|
disabled
|
|
css={STYLES_HIDDEN_INPUT}
|
|
type="date"
|
|
name={this.props.name}
|
|
value={this.props.value}
|
|
onChange={this.props.onChange}
|
|
/>
|
|
<div>
|
|
<Input
|
|
label={this.props.label}
|
|
description={this.props.description}
|
|
tooltip={this.props.tooltip}
|
|
icon={SVG.Calendar}
|
|
max={10}
|
|
value={this.state.value}
|
|
placeholder="MM/DD/YYYY"
|
|
pattern="^[0-9\/]*$"
|
|
validation={this.state.validation}
|
|
onChange={this._handleChange}
|
|
onBlur={this._handleBlur}
|
|
onSubmit={this._handleCalendar}
|
|
/>
|
|
{this.state.cal ? (
|
|
<div css={STYLES_CALENDAR}>
|
|
<div css={STYLES_MONTH_CONTAINER}>
|
|
<SVG.ChevronLeft
|
|
height="20px"
|
|
css={STYLES_ICON}
|
|
onClick={() => this._incrementCal(-1)}
|
|
/>
|
|
<div css={STYLES_MONTH}>
|
|
{this.state.cal.isSame(moment(), "year")
|
|
? this.state.cal.format("MMMM")
|
|
: this.state.cal.format("MMMM YYYY")}
|
|
</div>
|
|
<SVG.ChevronRight
|
|
height="20px"
|
|
css={STYLES_ICON}
|
|
onClick={() => this._incrementCal(1)}
|
|
style={{ justifySelf: "end" }}
|
|
/>
|
|
</div>
|
|
<div css={STYLES_WEEKDAYS}>{weekdays}</div>
|
|
<div css={STYLES_DATES}>{month}</div>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|